瀏覽代碼

feat(admin): 新增1688商品批量导入功能

- 在路由文件中添加 POST /admin/alibaba/goods/import 接口
- 更新商品详情API文档链接为正确的接口地址
- 重构商品控制器方法注释返回类型并优化代码结构
- 在商品服务类中实现 batchImport 方法支持批量入库
- 添加商品详情获取功能并完善数据格式化逻辑
- 实现价格加价50%处理机制并记录入库日志
- 支持多商品ID批量处理和重复入库检查机制
shichen 3 月之前
父節點
當前提交
a7f946e6e7
共有 3 個文件被更改,包括 333 次插入186 次删除
  1. 55 11
      app/controller/admin/alibaba/Goods.php
  2. 277 175
      app/services/ThirdParty/AlibabaAgent/ProductService.php
  3. 1 0
      route/admin.php

+ 55 - 11
app/controller/admin/alibaba/Goods.php

@@ -9,7 +9,7 @@ use app\services\ThirdParty\AlibabaAgent\ProductService;
9 9
  *
10 10
  * API文档:
11 11
  *   商品列表: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.fenxiao%3Ajxhy.product.getPageList-1
12
- *   商品详情: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.fenxiao%3Aalibaba.fenxiao.productInfo.get-1
12
+ *   商品详情: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.fenxiao%3Aalibaba.pifatuan.product.detail.list-2
13 13
  */
14 14
 class Goods
15 15
 {
@@ -17,7 +17,7 @@ class Goods
17 17
      * 获取1688分销商品分页列表
18 18
      *
19 19
      * @param ProductService $productService
20
-     * @return \think\response\Json
20
+     * @return \think\Response
21 21
      *
22 22
      * @api {GET} /admin/alibaba/goods/lst 获取商品列表
23 23
      * @apiParam {string}  [access_token]  1688 access_token(不传则使用配置中的默认token)
@@ -33,7 +33,6 @@ class Goods
33 33
     {
34 34
         $request = app('request');
35 35
 
36
-        // 获取请求参数
37 36
         $page = (int)$request->param('page', 1);
38 37
         $limit = (int)$request->param('limit', 20);
39 38
         $keyword = $request->param('keyword', '');
@@ -42,7 +41,6 @@ class Goods
42 41
         $maxPrice = $request->param('maxPrice', '');
43 42
         $sort = $request->param('sort', '');
44 43
 
45
-        // 构建搜索参数
46 44
         $params = [
47 45
             'page' => max(1, $page),
48 46
             'limit' => min(max(1, $limit), 50),
@@ -51,24 +49,19 @@ class Goods
51 49
         if (!empty($keyword)) {
52 50
             $params['keyword'] = $keyword;
53 51
         }
54
-
55 52
         if (!empty($categoryId)) {
56 53
             $params['categoryId'] = (int)$categoryId;
57 54
         }
58
-
59 55
         if ($minPrice !== '' && $minPrice > 0) {
60 56
             $params['minPrice'] = (float)$minPrice;
61 57
         }
62
-
63 58
         if ($maxPrice !== '' && $maxPrice > 0) {
64 59
             $params['maxPrice'] = (float)$maxPrice;
65 60
         }
66
-
67 61
         if (!empty($sort)) {
68 62
             $params['sort'] = $sort;
69 63
         }
70 64
 
71
-        // 调用1688 API获取商品列表
72 65
         $result = $productService->searchProducts($params);
73 66
 
74 67
         if ($result === false) {
@@ -82,7 +75,7 @@ class Goods
82 75
      * 获取1688分销商品详情
83 76
      *
84 77
      * @param ProductService $productService
85
-     * @return \think\response\Json
78
+     * @return mixed
86 79
      *
87 80
      * @api {GET} /admin/alibaba/goods/detail 获取商品详情
88 81
      * @apiParam {string}  [access_token]  1688 access_token(不传则使用配置中的默认token)
@@ -98,7 +91,6 @@ class Goods
98 91
             return app('json')->fail('参数错误:productId不能为空');
99 92
         }
100 93
 
101
-        // 调用1688 API获取商品详情
102 94
         $result = $productService->getProductDetail($productId);
103 95
 
104 96
         if ($result === false) {
@@ -107,4 +99,56 @@ class Goods
107 99
 
108 100
         return app('json')->success($result);
109 101
     }
102
+
103
+    /**
104
+     * 批量导入1688商品到 alibaba_import_goods 表
105
+     *
106
+     * 业务场景:从1688商品列表中选择多个商品,一键入库
107
+     * 商品详情以JSON格式完整存储到 alibaba_import_goods 表
108
+     *
109
+     * @param ProductService $productService
110
+     * @return mixed
111
+     *
112
+     * @api {POST} /admin/alibaba/goods/import 批量入库
113
+     * @apiParam {string}  productIds  1688商品ID(必填),多个用逗号分隔,如:787533862434 或 787533862434,787533862435
114
+     */
115
+    public function import(ProductService $productService)
116
+    {
117
+        $request = app('request');
118
+
119
+        $raw = $request->param('productIds', '');
120
+
121
+        // 参数校验
122
+        if (empty($raw)) {
123
+            return app('json')->fail('参数错误:productIds不能为空');
124
+        }
125
+
126
+        // 解析productIds:支持 "787533862434" 或 "787533862434,787533862435"
127
+        $productIds = explode(',', str_replace(' ', '', $raw));
128
+        $productIds = array_unique(array_filter($productIds, function ($v) {
129
+            return $v !== '' && $v !== null;
130
+        }));
131
+
132
+        if (empty($productIds)) {
133
+            return app('json')->fail('参数错误:productIds格式不正确');
134
+        }
135
+
136
+        // 执行批量入库
137
+        $result = $productService->batchImport($productIds, 0);
138
+
139
+        $successCount = count($result['success']);
140
+        $failCount = count($result['fail']);
141
+
142
+        if ($successCount > 0) {
143
+            return app('json')->success([
144
+                'success'       => $result['success'],
145
+                'fail'          => $result['fail'],
146
+                'success_count' => $successCount,
147
+                'fail_count'    => $failCount,
148
+                'message'       => "成功入库 {$successCount} 个商品" . ($failCount > 0 ? ",{$failCount} 个失败" : ''),
149
+            ]);
150
+        }
151
+
152
+        return app('json')->fail('入库失败:' . implode('; ', array_values($result['fail'])));
153
+    }
110 154
 }

+ 277 - 175
app/services/ThirdParty/AlibabaAgent/ProductService.php

@@ -2,8 +2,7 @@
2 2
 /**
3 3
  * 1688分销严选采购解决方案 - 商品服务类
4 4
  *
5
- * 提供商品搜索/列表查询功能
6
- * API文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.fenxiao%3Ajxhy.product.getPageList-1&aopApiCategory=category_new
5
+ * 提供商品搜索/列表查询、详情获取、批量入库等功能
7 6
  *
8 7
  * @author: yourname
9 8
  * @day: 2026/04/27
@@ -11,6 +10,9 @@
11 10
 
12 11
 namespace app\services\ThirdParty\AlibabaAgent;
13 12
 
13
+use think\facade\Db;
14
+use think\facade\Log;
15
+
14 16
 class ProductService extends AlibabaAgentBaseService
15 17
 {
16 18
     /**
@@ -25,33 +27,20 @@ class ProductService extends AlibabaAgentBaseService
25 27
     {
26 28
         $businessParams = [];
27 29
 
28
-        // 关键词搜索
29 30
         if (!empty($params['keyword'])) {
30 31
             $businessParams['keyword'] = $params['keyword'];
31 32
         }
32
-
33
-        // 类目ID
34 33
         if (!empty($params['categoryId'])) {
35 34
             $businessParams['categoryId'] = $params['categoryId'];
36 35
         }
37
-
38
-        // 页码,默认1
39 36
         $businessParams['pageNo'] = $params['page'] ?? 1;
40
-
41
-        // 每页条数,最大50
42 37
         $businessParams['pageSize'] = min($params['limit'] ?? 20, 50);
43
-
44
-        // 排序方式
45 38
         if (!empty($params['sort'])) {
46 39
             $businessParams['sort'] = $params['sort'];
47 40
         }
48
-
49
-        // 最低价格过滤(仅当 > 0 时传入)
50 41
         if (!empty($params['minPrice']) && $params['minPrice'] > 0) {
51 42
             $businessParams['priceStart'] = $params['minPrice'];
52 43
         }
53
-
54
-        // 最高价格过滤(仅当 > 0 时传入)
55 44
         if (!empty($params['maxPrice']) && $params['maxPrice'] > 0) {
56 45
             $businessParams['priceEnd'] = $params['maxPrice'];
57 46
         }
@@ -79,30 +68,16 @@ class ProductService extends AlibabaAgentBaseService
79 68
     protected function formatSearchResult(array $result): array
80 69
     {
81 70
         $productList = [];
82
-
83
-        // 1688 API返回结构:
84
-        // {
85
-        //   "result": {
86
-        //     "success": true,
87
-        //     "code": "0",
88
-        //     "result": [ {商品1}, {商品2}, ... ],  // 商品列表
89
-        //     "pageInfo": {
90
-        //       "currentPage": 1,
91
-        //       "totalRecords": 2000,
92
-        //       "pageSize": 10
93
-        //     }
94
-        //   }
95
-        // }
96 71
         $data = $result['result'] ?? $result;
97
-        $products = $data['result'] ?? [];  // 商品列表在 result.result 中
72
+        $products = $data['result'] ?? [];
98 73
         $pageInfo = $data['pageInfo'] ?? [];
99 74
 
100 75
         if (empty($products)) {
101 76
             return [
102
-                'total' => 0,
103
-                'page' => (int)($pageInfo['currentPage'] ?? 1),
77
+                'total'    => 0,
78
+                'page'     => (int)($pageInfo['currentPage'] ?? 1),
104 79
                 'pageSize' => (int)($pageInfo['pageSize'] ?? 20),
105
-                'list' => [],
80
+                'list'     => [],
106 81
             ];
107 82
         }
108 83
 
@@ -111,10 +86,10 @@ class ProductService extends AlibabaAgentBaseService
111 86
         }
112 87
 
113 88
         return [
114
-            'total' => (int)($pageInfo['totalRecords'] ?? count($productList)),
115
-            'page' => (int)($pageInfo['currentPage'] ?? 1),
89
+            'total'    => (int)($pageInfo['totalRecords'] ?? count($productList)),
90
+            'page'     => (int)($pageInfo['currentPage'] ?? 1),
116 91
             'pageSize' => (int)($pageInfo['pageSize'] ?? 20),
117
-            'list' => $productList,
92
+            'list'     => $productList,
118 93
         ];
119 94
     }
120 95
 
@@ -126,13 +101,8 @@ class ProductService extends AlibabaAgentBaseService
126 101
      */
127 102
     public function formatProduct(array $product): array
128 103
     {
129
-        // 1688分销商品列表API返回字段:
130
-        // itemId, title, imgUrl, minPrice, maxPrice, salesCnt90d, skuCnt, serviceList
131
-
132
-        // 主图
133 104
         $mainImage = $product['imgUrl'] ?? $product['mainImage'] ?? $product['mainPicture'] ?? '';
134 105
 
135
-        // 服务标签
136 106
         $serviceTags = [];
137 107
         if (!empty($product['serviceList'])) {
138 108
             foreach ($product['serviceList'] as $service) {
@@ -141,62 +111,44 @@ class ProductService extends AlibabaAgentBaseService
141 111
         }
142 112
 
143 113
         return [
144
-            // 基础信息
145
-            'productId' => $product['itemId'] ?? $product['productId'] ?? $product['offerId'] ?? $product['id'] ?? 0,
146
-            'productType' => $product['productType'] ?? '',
147
-            'categoryId' => $product['categoryId'] ?? $product['catId'] ?? 0,
148
-            'categoryName' => $product['categoryName'] ?? '',
149
-
150
-            // 标题与描述
151
-            'title' => $product['title'] ?? $product['subject'] ?? $product['name'] ?? '',
152
-            'description' => $product['description'] ?? $product['detail'] ?? '',
153
-
154
-            // 价格信息
155
-            'price' => $product['minPrice'] ?? $product['price'] ?? $product['offerPrice'] ?? 0,
156
-            'salePrice' => $product['maxPrice'] ?? $product['salePrice'] ?? $product['price'] ?? 0,
157
-            'minPrice' => $product['minPrice'] ?? $product['price'] ?? 0,
158
-            'maxPrice' => $product['maxPrice'] ?? $product['price'] ?? 0,
159
-
160
-            // 库存信息(该接口不返回库存,用skuCnt作为参考)
161
-            'stock' => $product['stock'] ?? $product['totalAvailableStock'] ?? 0,
162
-            'unit' => $product['unit'] ?? '件',
163
-
164
-            // 图片信息
165
-            'mainImage' => $mainImage,
166
-            'imageList' => [$mainImage],
167
-
168
-            // SKU数量
169
-            'skuCnt' => $product['skuCnt'] ?? 0,
170
-
171
-            // 销售信息
172
-            'monthSales' => $product['monthSales'] ?? $product['salesCnt90d'] ?? 0,
173
-            'totalSales' => $product['totalSales'] ?? 0,
174
-
175
-            // 服务标签
176
-            'serviceTags' => $serviceTags,
177
-
178
-            // 分销属性
179
-            'isDistribution' => $product['isDistribution'] ?? false,
114
+            'productId'        => $product['itemId'] ?? $product['productId'] ?? $product['offerId'] ?? $product['id'] ?? 0,
115
+            'productType'      => $product['productType'] ?? '',
116
+            'categoryId'       => $product['categoryId'] ?? $product['catId'] ?? 0,
117
+            'categoryName'     => $product['categoryName'] ?? '',
118
+            'title'            => $product['title'] ?? $product['subject'] ?? $product['name'] ?? '',
119
+            'description'      => $product['description'] ?? $product['detail'] ?? '',
120
+            'price'            => $product['minPrice'] ?? $product['price'] ?? $product['offerPrice'] ?? 0,
121
+            'salePrice'        => $product['maxPrice'] ?? $product['salePrice'] ?? $product['price'] ?? 0,
122
+            'minPrice'         => $product['minPrice'] ?? $product['price'] ?? 0,
123
+            'maxPrice'         => $product['maxPrice'] ?? $product['price'] ?? 0,
124
+            'stock'            => $product['stock'] ?? $product['totalAvailableStock'] ?? 0,
125
+            'unit'             => $product['unit'] ?? '件',
126
+            'mainImage'        => $mainImage,
127
+            'imageList'        => [$mainImage],
128
+            'skuCnt'           => $product['skuCnt'] ?? 0,
129
+            'monthSales'       => $product['monthSales'] ?? $product['salesCnt90d'] ?? 0,
130
+            'totalSales'       => $product['totalSales'] ?? 0,
131
+            'serviceTags'      => $serviceTags,
132
+            'isDistribution'   => $product['isDistribution'] ?? false,
180 133
             'isStrictSelected' => $product['isStrictSelected'] ?? false,
181 134
             'distributorPrice' => $product['distributorPrice'] ?? 0,
182
-
183
-            // 店铺信息(该接口不返回店铺信息)
184
-            'shopName' => $product['shopName'] ?? $product['companyName'] ?? '',
185
-            'shopId' => $product['shopId'] ?? $product['memberId'] ?? '',
186
-
187
-            // 链接
188
-            'detailUrl' => $product['detailUrl'] ?? $product['offerUrl'] ?? '',
135
+            'shopName'         => $product['shopName'] ?? $product['companyName'] ?? '',
136
+            'shopId'           => $product['shopId'] ?? $product['memberId'] ?? '',
137
+            'detailUrl'        => $product['detailUrl'] ?? $product['offerUrl'] ?? '',
189 138
         ];
190 139
     }
191 140
 
141
+    // ================================================================
142
+    //  商品详情
143
+    // ================================================================
144
+
192 145
     /**
193 146
      * 获取分销商品详情(含SKU、图片等完整信息)
194 147
      *
195 148
      * API: com.alibaba.fenxiao:alibaba.pifatuan.product.detail.list-2
196
-     * 文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.fenxiao%3Aalibaba.pifatuan.product.detail.list-2
197 149
      *
198 150
      * @param int|string $productId 1688商品ID
199
-     * @return array|false
151
+     * @return array|false 格式化后的商品详情数据
200 152
      */
201 153
     public function getProductDetail($productId)
202 154
     {
@@ -205,7 +157,6 @@ class ProductService extends AlibabaAgentBaseService
205 157
             return false;
206 158
         }
207 159
 
208
-        // API要求参数名为 offerIds,类型为 Long[](数组)
209 160
         $businessParams = [
210 161
             'offerIds' => '[' . $productId . ']',
211 162
         ];
@@ -221,23 +172,33 @@ class ProductService extends AlibabaAgentBaseService
221 172
             return false;
222 173
         }
223 174
 
224
-        // 临时:直接返回原始数据,方便查看结构
225
-        return $result;
175
+        return $this->formatDetailResult($result);
226 176
     }
227 177
 
228 178
     /**
229 179
      * 格式化商品详情结果
230 180
      *
231
-     * 1688商品详情API返回结构:
181
+     * 实际API返回结构:
232 182
      * {
233 183
      *   "result": {
234 184
      *     "success": true,
235
-     *     "code": "0",
236
-     *     "result": {
237
-     *       "productInfo": { 商品详细信息 },
238
-     *       "skuList": [ SKU列表 ],
239
-     *       "productImgList": [ 图片列表 ]
240
-     *     }
185
+     *     "result": [
186
+     *       {
187
+     *         "wangwangAccount": "...",
188
+     *         "productInfo": {
189
+     *           "productID": 635835395997,
190
+     *           "subject": "商品标题",
191
+     *           "image": { "images": ["img/ibank/..."] },
192
+     *           "saleInfo": { "amountOnSale": 536989, "priceRanges": [...], "unit": "PCS" },
193
+     *           "skuInfos": [{ "skuId": 5580608617712, "consignPrice": 4.14, "pifaPrice": 0.14, ... }],
194
+     *           "categoryID": 202060914,
195
+     *           "categoryName": "晶体振荡器",
196
+     *           "description": "<div>..."
197
+     *         },
198
+     *         "tagInfoList": ["一件代发", "一件代发包邮"],
199
+     *         "sellerOpenUid": "BBBAAvFPtiSWOJWNj2-MpLo4w"
200
+     *       }
201
+     *     ]
241 202
      *   }
242 203
      * }
243 204
      *
@@ -247,103 +208,244 @@ class ProductService extends AlibabaAgentBaseService
247 208
     protected function formatDetailResult(array $result): array
248 209
     {
249 210
         $data = $result['result'] ?? $result;
250
-        $detailData = $data['result'] ?? [];
211
+        $items = $data['result'] ?? [];
251 212
 
252
-        if (empty($detailData)) {
213
+        if (empty($items) || !is_array($items)) {
253 214
             return [];
254 215
         }
255 216
 
256
-        $productInfo = $detailData['productInfo'] ?? $detailData;
257
-        $skuList = $detailData['skuList'] ?? [];
258
-        $productImgList = $detailData['productImgList'] ?? [];
217
+        // 取第一个商品(API按offerIds查询单个商品)
218
+        $item = $items[0] ?? [];
219
+        if (empty($item)) {
220
+            return [];
221
+        }
259 222
 
260
-        // 格式化SKU
261
-        $formattedSkus = [];
262
-        if (!empty($skuList)) {
263
-            foreach ($skuList as $sku) {
264
-                $formattedSkus[] = [
265
-                    'skuId' => $sku['skuId'] ?? '',
266
-                    'specId' => $sku['specId'] ?? '',
267
-                    'price' => $sku['price'] ?? $sku['retailPrice'] ?? 0,
268
-                    'salePrice' => $sku['salePrice'] ?? $sku['price'] ?? 0,
269
-                    'stock' => $sku['stock'] ?? $sku['availableStock'] ?? 0,
270
-                    'specName' => $sku['specName'] ?? $sku['name'] ?? '',
271
-                    'image' => $sku['image'] ?? '',
272
-                ];
223
+        $productInfo = $item['productInfo'] ?? [];
224
+        $tagInfoList = $item['tagInfoList'] ?? [];
225
+        $wangwangAccount = $item['wangwangAccount'] ?? '';
226
+
227
+        // --- 图片处理 ---
228
+        $imageData = $productInfo['image'] ?? [];
229
+        $rawImages = $imageData['images'] ?? [];
230
+        $imageList = [];
231
+        $mainImage = '';
232
+        if (!empty($rawImages)) {
233
+            foreach ($rawImages as $img) {
234
+                $fullUrl = $this->buildImageUrl($img);
235
+                $imageList[] = $fullUrl;
236
+                if (empty($mainImage)) {
237
+                    $mainImage = $fullUrl;
238
+                }
273 239
             }
274 240
         }
275 241
 
276
-        // 格式化图片列表
277
-        $imageList = [];
278
-        if (!empty($productImgList)) {
279
-            foreach ($productImgList as $img) {
280
-                $url = is_string($img) ? $img : ($img['url'] ?? $img['imgUrl'] ?? '');
281
-                if (!empty($url)) {
282
-                    $imageList[] = $url;
242
+        // --- 价格处理 ---
243
+        $saleInfo = $productInfo['saleInfo'] ?? [];
244
+        $priceRanges = $saleInfo['priceRanges'] ?? [];
245
+        $minPrice = 0;
246
+        $maxPrice = 0;
247
+        if (!empty($priceRanges)) {
248
+            $prices = array_column($priceRanges, 'price');
249
+            $minPrice = !empty($prices) ? (float)min($prices) : 0;
250
+            $maxPrice = !empty($prices) ? (float)max($prices) : 0;
251
+        }
252
+
253
+        // --- 商品属性(如品牌、货号、规格等) ---
254
+        $attributes = $productInfo['attributes'] ?? [];
255
+
256
+        // --- SKU处理(提取规格名称、规格值、成本价) ---
257
+        $skuInfos = $productInfo['skuInfos'] ?? [];
258
+        $formattedSkus = [];
259
+        if (!empty($skuInfos)) {
260
+            foreach ($skuInfos as $sku) {
261
+                // 提取规格属性(规则名称 + 规格值名称)
262
+                $skuAttrs = $sku['attributes'] ?? [];
263
+                $specNames = [];
264
+                $specValues = [];
265
+                $skuAttrList = [];
266
+                foreach ($skuAttrs as $attr) {
267
+                    $attrName = $attr['attributeName'] ?? '';
268
+                    $attrValue = $attr['attributeValue'] ?? '';
269
+                    if ($attrName !== '') {
270
+                        $specNames[] = $attrName;
271
+                        $specValues[] = $attrValue;
272
+                        $skuAttrList[] = [
273
+                            'name'  => $attrName,
274
+                            'value' => $attrValue,
275
+                        ];
276
+                    }
283 277
                 }
278
+
279
+                // 成本价(consignPrice 是分销价/代发价)
280
+                $costPrice = (float)($sku['consignPrice'] ?? $sku['pifaPrice'] ?? 0);
281
+
282
+                $formattedSkus[] = [
283
+                    'skuId'        => $sku['skuId'] ?? '',
284
+                    'specId'       => $sku['specId'] ?? '',
285
+                    'consignPrice' => $costPrice,
286
+                    'pifaPrice'    => (float)($sku['pifaPrice'] ?? 0),
287
+                    'costPrice'    => $costPrice,
288
+                    'stock'        => $sku['amountOnSale'] ?? 0,
289
+                    'cargoNumber'  => $sku['cargoNumber'] ?? '',
290
+                    // 规格信息
291
+                    'specNames'    => $specNames,
292
+                    'specValues'   => $specValues,
293
+                    'specInfo'     => !empty($skuAttrList) ? json_encode($skuAttrList, JSON_UNESCAPED_UNICODE) : ($sku['specInfo'] ?? ''),
294
+                    'skuAttrs'     => $skuAttrList,
295
+                    // 规格图片
296
+                    'image'        => !empty($skuAttrs[0]['skuImageUrl'] ?? '') ? $this->buildImageUrl($skuAttrs[0]['skuImageUrl']) : '',
297
+                ];
284 298
             }
285 299
         }
286 300
 
287
-        // 主图
288
-        $mainImage = $productInfo['imgUrl'] ?? $productInfo['mainImage'] ?? $productInfo['mainPicture'] ?? '';
289
-        if (empty($mainImage) && !empty($imageList)) {
290
-            $mainImage = $imageList[0];
301
+        // --- 描述处理 ---
302
+        $description = $productInfo['description'] ?? '';
303
+
304
+        return [
305
+            'productId'     => $productInfo['productID'] ?? $productInfo['productId'] ?? 0,
306
+            'categoryId'    => $productInfo['categoryID'] ?? $productInfo['categoryId'] ?? 0,
307
+            'categoryName'  => $productInfo['categoryName'] ?? '',
308
+            'title'         => $productInfo['subject'] ?? '',
309
+            'description'   => $description,
310
+            'price'         => $minPrice,
311
+            'minPrice'      => $minPrice,
312
+            'maxPrice'      => $maxPrice,
313
+            'stock'         => $saleInfo['amountOnSale'] ?? 0,
314
+            'unit'          => $saleInfo['unit'] ?? '件',
315
+            'mainImage'     => $mainImage,
316
+            'imageList'     => $imageList,
317
+            'attributes'    => $attributes,
318
+            'skuList'       => $formattedSkus,
319
+            'shopName'      => $wangwangAccount,
320
+            'sellerOpenUid' => $item['sellerOpenUid'] ?? '',
321
+            'serviceTags'   => $tagInfoList,
322
+        ];
323
+    }
324
+
325
+    /**
326
+     * 构建图片完整URL
327
+     *
328
+     * @param string $path 图片路径
329
+     * @return string
330
+     */
331
+    protected function buildImageUrl(string $path): string
332
+    {
333
+        if (empty($path)) {
334
+            return '';
335
+        }
336
+        if (strpos($path, 'http://') === 0 || strpos($path, 'https://') === 0) {
337
+            return $path;
291 338
         }
339
+        // 1688 图片路径为相对路径如 img/ibank/O1CN01...,需拼接阿里云CDN域名
340
+        return 'https://cbu01.alicdn.com/' . ltrim($path, '/');
341
+    }
292 342
 
293
-        // 服务标签
294
-        $serviceTags = [];
295
-        if (!empty($productInfo['serviceList'])) {
296
-            foreach ($productInfo['serviceList'] as $service) {
297
-                $serviceTags[] = $service['name'] ?? '';
343
+    // ================================================================
344
+    //  批量入库
345
+    // ================================================================
346
+
347
+    /**
348
+     * 对价格进行加价处理(加价50%)
349
+     *
350
+     * @param float $price 原价
351
+     * @return float 加价后的价格
352
+     */
353
+    protected function applyMarkup(float $price): float
354
+    {
355
+        return round($price * 1.5, 2);
356
+    }
357
+
358
+    /**
359
+     * 批量导入1688商品到 alibaba_import_goods 表
360
+     *
361
+     * 业务场景:从1688商品列表中选择多个商品,一键入库
362
+     * 将商品详情数据以JSON格式完整存储到 alibaba_import_goods 表
363
+     * 入库时自动对所有价格加价50%
364
+     *
365
+     * @param array $productIds 1688商品ID数组,如 [635835395997, 635835395998]
366
+     * @param int   $merId      商户ID
367
+     * @param array $extra      额外参数(预留)
368
+     * @return array ['success' => [productId => localId], 'fail' => [productId => reason]]
369
+     */
370
+    public function batchImport(array $productIds, int $merId, array $extra = []): array
371
+    {
372
+        $success = [];
373
+        $fail = [];
374
+        $now = date('Y-m-d H:i:s');
375
+
376
+        foreach ($productIds as $productId) {
377
+            try {
378
+                // 检查是否已入库(product_id唯一)
379
+                $exists = Db::name('alibaba_import_goods')
380
+                    ->where('product_id', (int)$productId)
381
+                    ->where('status', 1)
382
+                    ->find();
383
+
384
+                if ($exists) {
385
+                    $success[$productId] = $exists['id'];
386
+                    Log::info("[1688入库] 商品{$productId}已存在,跳过入库,本地ID:{$exists['id']}");
387
+                    continue;
388
+                }
389
+
390
+                // 调用详情API获取完整数据
391
+                $detail = $this->getProductDetail($productId);
392
+                if ($detail === false || empty($detail)) {
393
+                    $fail[$productId] = '获取1688商品详情失败';
394
+                    continue;
395
+                }
396
+
397
+                // ===== 加价处理:所有价格加价50% =====
398
+                // 1. SKU级别成本价加价
399
+                $skuList = $detail['skuList'] ?? [];
400
+                foreach ($skuList as &$sku) {
401
+                    $sku['originalCostPrice'] = $sku['costPrice'] ?? 0;
402
+                    $sku['costPrice'] = $this->applyMarkup((float)$sku['costPrice']);
403
+                    $sku['originalConsignPrice'] = $sku['consignPrice'] ?? 0;
404
+                    $sku['consignPrice'] = $this->applyMarkup((float)$sku['consignPrice']);
405
+                    $sku['originalPifaPrice'] = $sku['pifaPrice'] ?? 0;
406
+                    $sku['pifaPrice'] = $this->applyMarkup((float)$sku['pifaPrice']);
407
+                }
408
+                unset($sku);
409
+                $detail['skuList'] = $skuList;
410
+
411
+                // 2. 商品级别价格加价
412
+                $detail['originalMinPrice'] = $detail['minPrice'] ?? 0;
413
+                $detail['originalMaxPrice'] = $detail['maxPrice'] ?? 0;
414
+                $detail['originalPrice'] = $detail['price'] ?? 0;
415
+                $detail['minPrice'] = $this->applyMarkup((float)$detail['minPrice']);
416
+                $detail['maxPrice'] = $this->applyMarkup((float)$detail['maxPrice']);
417
+                $detail['price'] = $this->applyMarkup((float)$detail['price']);
418
+
419
+                // 3. 标记加价比例
420
+                $detail['markupRate'] = 50;
421
+                $detail['markupMultiplier'] = 1.5;
422
+
423
+                // 写入 alibaba_import_goods 表(完整JSON存储,含加价后价格)
424
+                $insertData = [
425
+                    'product_id'   => (int)$productId,
426
+                    'product_name' => $detail['title'] ?? '',
427
+                    'image'        => $detail['mainImage'] ?? '',
428
+                    'product_info' => json_encode($detail, JSON_UNESCAPED_UNICODE),
429
+                    'status'       => 1,
430
+                    'create_time'  => $now,
431
+                ];
432
+                $localId = Db::name('alibaba_import_goods')->insertGetId($insertData);
433
+
434
+                if ($localId) {
435
+                    $success[$productId] = $localId;
436
+                    Log::info("[1688入库] 商品{$productId}入库成功,本地ID:{$localId},已加价50%");
437
+                } else {
438
+                    $fail[$productId] = '写入数据库失败';
439
+                }
440
+            } catch (\Throwable $e) {
441
+                $fail[$productId] = '异常: ' . $e->getMessage();
442
+                Log::error("[1688入库] 商品{$productId}入库异常: " . $e->getMessage());
298 443
             }
299 444
         }
300 445
 
301 446
         return [
302
-            // 基础信息
303
-            'productId' => $productInfo['itemId'] ?? $productInfo['productId'] ?? $productInfo['offerId'] ?? 0,
304
-            'productType' => $productInfo['productType'] ?? '',
305
-            'categoryId' => $productInfo['categoryId'] ?? $productInfo['catId'] ?? 0,
306
-            'categoryName' => $productInfo['categoryName'] ?? '',
307
-
308
-            // 标题与描述
309
-            'title' => $productInfo['title'] ?? $productInfo['subject'] ?? $productInfo['name'] ?? '',
310
-            'description' => $productInfo['description'] ?? $productInfo['detail'] ?? '',
311
-
312
-            // 价格信息
313
-            'price' => $productInfo['minPrice'] ?? $productInfo['price'] ?? $productInfo['offerPrice'] ?? 0,
314
-            'salePrice' => $productInfo['maxPrice'] ?? $productInfo['salePrice'] ?? $productInfo['price'] ?? 0,
315
-            'minPrice' => $productInfo['minPrice'] ?? $productInfo['price'] ?? 0,
316
-            'maxPrice' => $productInfo['maxPrice'] ?? $productInfo['price'] ?? 0,
317
-
318
-            // 库存信息
319
-            'stock' => $productInfo['stock'] ?? $productInfo['totalAvailableStock'] ?? 0,
320
-            'unit' => $productInfo['unit'] ?? '件',
321
-
322
-            // 图片信息
323
-            'mainImage' => $mainImage,
324
-            'imageList' => $imageList,
325
-
326
-            // SKU列表
327
-            'skuList' => $formattedSkus,
328
-
329
-            // 销售信息
330
-            'monthSales' => $productInfo['monthSales'] ?? $productInfo['salesCnt90d'] ?? 0,
331
-            'totalSales' => $productInfo['totalSales'] ?? 0,
332
-
333
-            // 服务标签
334
-            'serviceTags' => $serviceTags,
335
-
336
-            // 分销属性
337
-            'isDistribution' => $productInfo['isDistribution'] ?? false,
338
-            'isStrictSelected' => $productInfo['isStrictSelected'] ?? false,
339
-            'distributorPrice' => $productInfo['distributorPrice'] ?? 0,
340
-
341
-            // 店铺信息
342
-            'shopName' => $productInfo['shopName'] ?? $productInfo['companyName'] ?? '',
343
-            'shopId' => $productInfo['shopId'] ?? $productInfo['memberId'] ?? '',
344
-
345
-            // 链接
346
-            'detailUrl' => $productInfo['detailUrl'] ?? $productInfo['offerUrl'] ?? '',
447
+            'success' => $success,
448
+            'fail'    => $fail,
347 449
         ];
348 450
     }
349 451
 }

+ 1 - 0
route/admin.php

@@ -717,6 +717,7 @@ Route::group(config('admin.api_admin_prefix') . '/', function () {
717 717
         Route::group('alibaba/goods',function(){
718 718
             Route::get('lst', '/lst')->name('alibabaGoodsLst');
719 719
             Route::get('detail', '/detail')->name('alibabaGoodsDetail');
720
+            Route::post('import', '/import')->name('alibabaGoodsImport');
720 721
         })->prefix('admin.alibaba.Goods');
721 722
 
722 723