Просмотр исходного кода

命令行-新增拉取微唯宝商品逻辑-参考app\controller\api\douhuomall\Take.php-get_goods_list方法

sunxbiao месяцев назад: 6
Родитель
Сommit
b8c1112225
2 измененных файлов с 772 добавлено и 0 удалено
  1. 757 0
      app/command/WiwibaoProductSync.php
  2. 15 0
      app/common/enum/douhuomall/DouhuomallEnum.php

+ 757 - 0
app/command/WiwibaoProductSync.php

@@ -0,0 +1,757 @@
1
+<?php
2
+declare (strict_types=1);
3
+
4
+namespace app\command;
5
+
6
+use app\common\enum\douhuomall\DouhuomallEnum;
7
+use app\traits\GongApiRequest;
8
+use think\console\Command;
9
+use think\console\Input;
10
+use think\console\Output;
11
+use think\facade\Db;
12
+use think\facade\Log;
13
+
14
+class WiwibaoProductSync extends Command
15
+{
16
+
17
+    use GongApiRequest;
18
+
19
+    // 配置参数
20
+    protected $config = [
21
+        'page_size' => 100,      // 每页拉取数量
22
+        'max_pages' => 0,       // 最大页数限制
23
+        'price_change_rate' => 0.1, // 价格变动比率阈值(10%)
24
+    ];
25
+
26
+    protected function configure()
27
+    {
28
+        // 指令配置
29
+        $this->setName('wiwibaoproductsync')
30
+            ->setDescription('the wiwibaoproductsync command');
31
+    }
32
+
33
+    protected function execute(Input $input, Output $output)
34
+    {
35
+        $output->writeln('定时任务执行中...');
36
+
37
+        $this->syncGoodsFromGong();
38
+
39
+        $output->writeln('定时任务执行完成');
40
+    }
41
+
42
+    /**
43
+     * 同步微唯宝商品数据(主方法)
44
+     */
45
+    public function syncGoodsFromGong()
46
+    {
47
+        try {
48
+            set_time_limit(0);
49
+
50
+            // 1. 批量拉取所有商品数据
51
+            $allProducts = $this->fetchAllProducts();
52
+
53
+            if (empty($allProducts)) {
54
+                Log::info('未拉取到任何商品数据');
55
+                return 'success';
56
+            }
57
+
58
+            // 2. 获取本地所有SPU ID
59
+            $localSpuIds = Db::name('douhuomall')->column('spu_id');
60
+            $syncedSpuIds = [];
61
+
62
+            // 3. 批量处理商品数据
63
+            foreach ($allProducts as $product) {
64
+                try {
65
+                    $spuId = $this->processSingleProduct($product);
66
+                    if ($spuId) {
67
+                        $syncedSpuIds[] = $spuId;
68
+                    }
69
+                } catch (\Exception $e) {
70
+                    Log::error("处理商品 {$product['goods_id']} 失败: " . $e->getMessage());
71
+                    continue;
72
+                }
73
+            }
74
+
75
+            // 4. 处理需要下架的商品(本地有但第三方已下架)
76
+            $this->handleOfflineProducts($localSpuIds, $syncedSpuIds);
77
+
78
+            return 'success';
79
+
80
+        } catch (\Exception $e) {
81
+            $this->logError('商品同步失败', $e);
82
+            return 'error';
83
+        }
84
+    }
85
+
86
+    /**
87
+     * 批量拉取所有商品数据
88
+     */
89
+    protected function fetchAllProducts()
90
+    {
91
+        $allData = [];
92
+
93
+        // 第一页
94
+        $firstPageData = GongApiRequest::get_goods_list(1, $this->config['page_size']);
95
+        if (empty($firstPageData['list'])) {
96
+            return [];
97
+        }
98
+
99
+        $allData = $firstPageData['list'];
100
+        $totalPages = $firstPageData['pages'] ?? 1;
101
+        if (!empty($this->config['max_pages'])) {
102
+            $totalPages = min($totalPages, $this->config['max_pages']);
103
+        }
104
+
105
+        // 并行获取剩余页面
106
+        for ($page = 2; $page <= $totalPages; $page++) {
107
+            $pageData = GongApiRequest::get_goods_list($page, $this->config['page_size']);
108
+            if (!empty($pageData['list'])) {
109
+                $allData = array_merge($allData, $pageData['list']);
110
+            }
111
+        }
112
+
113
+        return $allData;
114
+    }
115
+
116
+    /**
117
+     * 处理单个商品
118
+     */
119
+    protected function processSingleProduct($productData)
120
+    {
121
+        $goodsId = $productData['goods_id'];
122
+
123
+        // 1. 获取商品详情
124
+        $goodsInfo = GongApiRequest::get_goods_info($goodsId);
125
+        if (!$goodsInfo) {
126
+            return null;
127
+        }
128
+
129
+        // 2. 处理图片信息
130
+        $imageData = $this->processProductImages($goodsId, $goodsInfo);
131
+
132
+        // 3. 处理SKU信息
133
+        $skuInfo = $this->processSkuInfo($goodsInfo['sku_list'] ?? [], $goodsId);
134
+
135
+        // 4. 构建保存数据
136
+        $saveData = $this->buildSaveData($goodsInfo, $skuInfo, $imageData);
137
+
138
+        // 5. 保存到本地(使用原有的add方法逻辑)
139
+        $spuId = $this->addToDouhuomall($saveData);
140
+
141
+        // 6. 检查并处理已上架商品的价格变动
142
+        $this->checkAndProcessOnlineProduct($spuId, $saveData);
143
+
144
+        return $spuId;
145
+    }
146
+
147
+    /**
148
+     * 处理商品图片信息
149
+     */
150
+    protected function processProductImages($goodsId, $goodsInfo)
151
+    {
152
+        // 如果你需要保存图片到本地,可以在这里实现
153
+        // 否则直接返回URL
154
+        return [
155
+            'cover_url' => $goodsInfo['main_img'] ?? '',
156
+            'detail_images' => $goodsInfo['detail_img'] ?? [],
157
+            'detail_img_list' => $goodsInfo['detail_img_list'] ?? []
158
+        ];
159
+    }
160
+
161
+    /**
162
+     * 处理SKU信息
163
+     */
164
+    protected function processSkuInfo($skuList, $goodsId)
165
+    {
166
+        if (empty($skuList)) {
167
+            return [];
168
+        }
169
+
170
+        $processedSkus = [];
171
+        foreach ($skuList as $sku) {
172
+            if (empty($sku['plat_price'])) {
173
+                continue;
174
+            }
175
+
176
+            $processedSku = [
177
+                'sku_id' => $sku['sku_id'] ?? 0,
178
+                'plat_price' => $sku['plat_price'],
179
+                'img_url' => $sku['img_url'],
180
+                'retail_price' => $sku['retail_price'] ?? 0,
181
+                'cost_price' => $this->calculateCostPrice($sku['plat_price']),
182
+                'market_price' => $this->calculateMarketPrice($sku['plat_price']),
183
+                'profit' => $this->calculateProfit($sku['plat_price']),
184
+                'mer_profit' => $this->calculateMerProfit($sku['plat_price']),
185
+                'yanglaojin' => '0.00',
186
+                'main_img' => $sku['main_img'] ?? '',
187
+                'attr' => !empty($sku['attr']) ? $sku['attr'] : []
188
+            ];
189
+
190
+            $processedSkus[] = array_merge($sku, $processedSku);
191
+        }
192
+
193
+        return $processedSkus;
194
+    }
195
+
196
+    /**
197
+     * 构建保存数据
198
+     */
199
+    protected function buildSaveData($goodsInfo, $skuInfo, $imageData)
200
+    {
201
+        // 构建详情HTML
202
+        $detailHtml = $this->buildDetailHtml($imageData['detail_images']);
203
+
204
+        return [
205
+            'spu_id' => $goodsInfo['goods_id'],
206
+            'skuId_info' => $skuInfo,
207
+            'sys_id' => '112',
208
+            'goods_id' => $goodsInfo['goods_id'],
209
+            'spuId_info' => array_merge(
210
+                $goodsInfo,
211
+                [
212
+                    'cover_url' => $imageData['cover_url'],
213
+                    'detail' => $detailHtml,
214
+                    'detail_img_list' => $imageData['detail_img_list']
215
+                ]
216
+            ),
217
+            'sku_id' => $skuInfo[0]['sku_id'] ?? 0,
218
+            'status' => $goodsInfo['status'] ?? 0,
219
+            'cate_ids' => $goodsInfo['cate_ids'] ?? '',
220
+            'title' => $goodsInfo['spu_name'] ?? '',
221
+            'cxb_cate_id' => 0
222
+        ];
223
+    }
224
+
225
+    /**
226
+     * 构建详情HTML
227
+     */
228
+    protected function buildDetailHtml($detailImages)
229
+    {
230
+        if (empty($detailImages)) {
231
+            return '';
232
+        }
233
+
234
+        $detailHtml = '';
235
+        foreach ((array)$detailImages as $imgUrl) {
236
+            $detailHtml .= "<img src='{$imgUrl}' referrerpolicy='no-referrer' />";
237
+        }
238
+
239
+        return $detailHtml;
240
+    }
241
+
242
+    /**
243
+     * 保存到douhuomall表(基于原有add方法)
244
+     */
245
+    protected function addToDouhuomall($data)
246
+    {
247
+        try {
248
+            $findData = Db::name('douhuomall')->where('spu_id', $data['spu_id'])->find();
249
+
250
+            // 对SKU按成本价排序,获取最低成本价的SKU
251
+            $skuInfo = $this->arrSort($data['skuId_info'], 'cost_price', SORT_ASC);
252
+
253
+            // 计算价格字段
254
+            $data['cost_price'] = $skuInfo[0]['cost_price'] ?? 0;
255
+            $data['market_price'] = $skuInfo[0]['market_price'] ?? 0;
256
+            $data['ot_price'] = $skuInfo[0]['retail_price'] ?? 0;
257
+            $data['mer_profit'] = $skuInfo[0]['mer_profit'] ?? 0;
258
+            $data['pension'] = $skuInfo[0]['yanglaojin'] ?? 0;
259
+            $data['profit'] = (empty($data['cost_price']) || empty($data['market_price']))
260
+                ? 0
261
+                : bcdiv((string)$data['market_price'], (string)$data['cost_price'], 2);
262
+
263
+            $now = date('Y-m-d H:i:s');
264
+
265
+            if (empty($findData)) {
266
+                // 新增
267
+                $insertData = [
268
+                    'spu_id' => $data['spu_id'],
269
+                    'skuId_info' => json_encode($data['skuId_info'], JSON_UNESCAPED_UNICODE),
270
+                    'sys_id' => $data['sys_id'],
271
+                    'goods_id' => $data['goods_id'],
272
+                    'spuId_info' => json_encode($data['spuId_info'], JSON_UNESCAPED_UNICODE),
273
+                    'create_time' => $now,
274
+                    'update_time' => $now,
275
+                    'sku_id' => $data['sku_id'],
276
+                    'status' => $data['status'],
277
+                    'cate_ids' => $data['cate_ids'],
278
+                    'title' => $data['title'],
279
+                    'cost_price' => $data['cost_price'],
280
+                    'market_price' => $data['market_price'],
281
+                    'ot_price' => $data['ot_price'],
282
+                    'profit' => $data['profit'],
283
+                    'mer_profit' => $data['mer_profit'],
284
+                    'pension' => $data['pension'],
285
+                    'cxb_cate_id' => $data['cxb_cate_id']
286
+                ];
287
+                Db::name('douhuomall')->insert($insertData);
288
+            } else {
289
+                // 更新
290
+                // 检查是否需要恢复已删除商品的上架状态
291
+                $shouldRestore = $this->shouldRestoreProduct($findData, $data);
292
+                if ($shouldRestore) {
293
+                    $this->restoreProduct($data['spu_id']);
294
+                }
295
+                $updateData = [
296
+                    'status' => $data['status'],
297
+                    'update_time' => $now,
298
+                    'cost_price' => $data['cost_price'],
299
+                    'market_price' => $data['market_price'],
300
+                    'ot_price' => $data['ot_price'],
301
+                    'mer_profit' => $data['mer_profit'],
302
+                    'pension' => $data['pension'],
303
+                    'profit' => $data['profit'],
304
+                    'skuId_info' => json_encode($data['skuId_info'], JSON_UNESCAPED_UNICODE),
305
+                    'spuId_info' => json_encode($data['spuId_info'], JSON_UNESCAPED_UNICODE),
306
+                    'title' => $data['title'],
307
+                    'cate_ids' => $data['cate_ids'],
308
+                    'cxb_cate_id' => $data['cxb_cate_id']
309
+                ];
310
+                Db::name('douhuomall')
311
+                    ->where('spu_id', $findData['spu_id'])
312
+                    ->update($updateData);
313
+            }
314
+
315
+            return $data['spu_id'];
316
+
317
+        } catch (\Exception $e) {
318
+            Log::error("保存商品到douhuomall失败: " . $e->getMessage());
319
+            throw $e;
320
+        }
321
+    }
322
+
323
+    /**
324
+     * 检查是否需要恢复已删除商品的上架状态
325
+     */
326
+    protected function shouldRestoreProduct($findData, $newData)
327
+    {
328
+        // 原有逻辑:如果货物属于已被删除状态,并且新更新进来的状态非删除状态
329
+        // 并且成本价、零售价、市场价、利润率没有变动的话
330
+        if ($findData['status'] == DouhuomallEnum::STATUS['Delete']['code']
331
+            && $newData['status'] !== DouhuomallEnum::STATUS['Delete']['code']
332
+            && $newData['cost_price'] == $findData['cost_price']
333
+            && $newData['ot_price'] == $findData['ot_price']
334
+            && $newData['market_price'] == $findData['market_price']
335
+            && $newData['profit'] == $findData['profit']) {
336
+
337
+            // 检查是否有已下架的商品
338
+            $count = Db::name('store_product')
339
+                ->where('spu_id', $newData['spu_id'])
340
+                ->where('is_show', 0)
341
+                ->where('is_status', 0)
342
+                ->count();
343
+
344
+            return $count > 0;
345
+        }
346
+
347
+        return false;
348
+    }
349
+
350
+    /**
351
+     * 恢复商品上架状态
352
+     */
353
+    protected function restoreProduct($spuId)
354
+    {
355
+        Db::name('store_product')
356
+            ->where('spu_id', $spuId)
357
+            ->update(['is_show' => 1]);
358
+
359
+        Log::info("恢复商品上架状态: spu_id={$spuId}");
360
+    }
361
+
362
+    /**
363
+     * 检查并处理已上架商品
364
+     */
365
+    protected function checkAndProcessOnlineProduct($spuId, $syncData)
366
+    {
367
+        // 检查商品是否已上架
368
+        $product = Db::name('store_product')
369
+            ->where('spu_id', $spuId)
370
+            ->find();
371
+
372
+        if (!$product) {
373
+            return;
374
+        }
375
+
376
+        // 获取本地商品价格信息
377
+        $localPrices = $this->getLocalProductPrices($product['product_id']);
378
+
379
+        // 解析同步数据中的价格
380
+        $syncSkuInfo = $syncData['skuId_info'];
381
+        $syncPrices = $this->extractPricesFromSkuInfo($syncSkuInfo);
382
+
383
+        // 比较价格,判断是否需要下架
384
+        $priceChanged = $this->checkPriceChange($localPrices, $syncPrices);
385
+
386
+        if ($priceChanged) {
387
+            // 价格变动超过阈值,下架商品并记录
388
+            $this->offlineProductWithRecord($product, $syncData, $localPrices, $syncPrices);
389
+        } else {
390
+            // 价格未变动或变动很小,只更新图片
391
+            $this->updateProductImages($spuId);
392
+        }
393
+    }
394
+
395
+    /**
396
+     * 获取本地商品价格信息
397
+     */
398
+    protected function getLocalProductPrices($productId)
399
+    {
400
+        $product = Db::name('store_product')
401
+            ->where('product_id', $productId)
402
+            ->field('price, cost, ot_price')
403
+            ->find();
404
+
405
+        // 获取SKU价格
406
+        $skus = Db::name('store_product_attr_value')
407
+            ->where('product_id', $productId)
408
+            ->field('sku, price, cost')
409
+            ->select()
410
+            ->toArray();
411
+
412
+        return [
413
+            'product_price' => $product['price'] ?? 0,
414
+            'product_cost' => $product['cost'] ?? 0,
415
+            'product_ot_price' => $product['ot_price'] ?? 0,
416
+            'skus' => $skus
417
+        ];
418
+    }
419
+
420
+    /**
421
+     * 从SKU信息中提取价格
422
+     */
423
+    protected function extractPricesFromSkuInfo($skuInfo)
424
+    {
425
+        $prices = [];
426
+        foreach ($skuInfo as $sku) {
427
+            $prices[] = [
428
+                'sku_id' => $sku['sku_id'] ?? '',
429
+                'market_price' => $sku['market_price'] ?? 0,
430
+                'cost_price' => $sku['cost_price'] ?? 0,
431
+                'retail_price' => $sku['retail_price'] ?? 0
432
+            ];
433
+        }
434
+        return $prices;
435
+    }
436
+
437
+    /**
438
+     * 检查价格是否变动超过阈值
439
+     */
440
+    protected function checkPriceChange($localPrices, $syncPrices)
441
+    {
442
+        // 如果没有SKU,比较商品主价格
443
+        if (empty($localPrices['skus'])) {
444
+            $localPrice = $localPrices['product_price'];
445
+            $syncPrice = $syncPrices[0]['market_price'] ?? 0;
446
+
447
+            if ($localPrice > 0 && $syncPrice > 0) {
448
+                $changeRate = abs(($syncPrice - $localPrice) / $localPrice);
449
+                return $changeRate > $this->config['price_change_rate'];
450
+            }
451
+            return false;
452
+        }
453
+
454
+        // 如果有SKU,匹配SKU进行比较
455
+        foreach ($localPrices['skus'] as $localSku) {
456
+            foreach ($syncPrices as $syncSku) {
457
+                // 尝试匹配SKU
458
+                if ($this->matchSku($localSku, $syncSku)) {
459
+                    $localPrice = $localSku['price'] ?? 0;
460
+                    $syncPrice = $syncSku['market_price'] ?? 0;
461
+
462
+                    if ($localPrice > 0 && $syncPrice > 0) {
463
+                        $changeRate = abs(($syncPrice - $localPrice) / $localPrice);
464
+                        if ($changeRate > $this->config['price_change_rate']) {
465
+                            return true;
466
+                        }
467
+                    }
468
+                }
469
+            }
470
+        }
471
+
472
+        return false;
473
+    }
474
+
475
+    /**
476
+     * 匹配SKU(根据业务逻辑实现)
477
+     */
478
+    protected function matchSku($localSku, $syncSku)
479
+    {
480
+        // 根据SKU ID匹配
481
+        if (!empty($localSku['sku']) && !empty($syncSku['sku_id'])) {
482
+            return $localSku['sku'] == $syncSku['sku_id'];
483
+        }
484
+
485
+        // 可以根据其他属性匹配,如规格等
486
+        return false;
487
+    }
488
+
489
+    /**
490
+     * 下架商品并记录变动
491
+     */
492
+    protected function offlineProductWithRecord($product, $syncData, $localPrices, $syncPrices)
493
+    {
494
+        $productId = $product['product_id'];
495
+        $spuId = $product['spu_id'];
496
+
497
+        // 1. 收集变动前数据
498
+        $beforeData = [
499
+            'product_id' => $productId,
500
+            'spu_id' => $spuId,
501
+            'title' => $product['store_name'] ?? '',
502
+            'status' => $product['is_show'] ?? 0,
503
+            'price_info' => $localPrices,
504
+            'images' => [
505
+                'image' => $product['image'] ?? '',
506
+                'slider_image' => $product['slider_image'] ?? ''
507
+            ],
508
+            'record_time' => date('Y-m-d H:i:s')
509
+        ];
510
+
511
+        // 2. 下架商品
512
+        Db::name('store_product')
513
+            ->where('product_id', $productId)
514
+            ->update([
515
+                'is_show' => 0,
516
+                'offline_time' => time(),
517
+                'offline_reason' => '第三方平台价格变动'
518
+            ]);
519
+
520
+        // 3. 收集变动后数据
521
+        $afterData = [
522
+            'spu_id' => $spuId,
523
+            'sync_price_info' => $syncPrices,
524
+            'sync_title' => $syncData['title'],
525
+            'sync_status' => $syncData['status'],
526
+            'sync_time' => date('Y-m-d H:i:s')
527
+        ];
528
+
529
+        // 4. 记录变动日志
530
+        $this->recordChangeLog(
531
+            $productId,
532
+            $spuId,
533
+            'price_change',
534
+            '第三方平台价格变动超过阈值',
535
+            $beforeData,
536
+            $afterData
537
+        );
538
+
539
+        Log::info("商品下架: product_id={$productId}, spu_id={$spuId}, 原因: 价格变动");
540
+    }
541
+
542
+    /**
543
+     * 更新商品图片(基于原有updata_product_img方法)
544
+     */
545
+    protected function updateProductImages($spuId)
546
+    {
547
+        // 1. 验证该SPU是否已上架
548
+        $productId = Db::name('store_product')
549
+            ->where('spu_id', $spuId)
550
+            ->value('product_id');
551
+
552
+        if (!$productId) {
553
+            return true;
554
+        }
555
+
556
+        // 2. 查找微唯宝商品
557
+        $data = Db::name('douhuomall')
558
+            ->where('spu_id', $spuId)
559
+            ->find();
560
+
561
+        if (!$data) {
562
+            return false;
563
+        }
564
+
565
+        // 3. 解析商品信息
566
+        $spuIdInfo = json_decode($data['spuId_info'], true);
567
+
568
+        // 4. 处理图片数据
569
+        $sliderImage = $spuIdInfo['detail_img_list'] ?? '';
570
+        $image = $spuIdInfo['cover_url'] ?? '';
571
+
572
+        try {
573
+            $sliderImage = implode(',', json_decode($sliderImage, true));
574
+        } catch (\Exception $e) {
575
+            $sliderImage = $image;
576
+        }
577
+
578
+        // 5. 更新产品图片数据
579
+        $updateData = [
580
+            'image' => $image,
581
+            'slider_image' => $sliderImage,
582
+        ];
583
+
584
+        Db::name('store_product')
585
+            ->where('product_id', $productId)
586
+            ->update($updateData);
587
+
588
+        // 6. 更新商品详情
589
+        $detail = $spuIdInfo['detail'] ?? '';
590
+        if (!is_null(json_decode($detail))) {
591
+            $detailList = json_decode($detail);
592
+            $newDetail = '';
593
+            foreach ($detailList as $value) {
594
+                $newDetail .= "<img src='{$value}' referrerpolicy='no-referrer' /></img>";
595
+            }
596
+            $detail = $newDetail;
597
+        }
598
+
599
+        Db::name('store_product_content')
600
+            ->where('product_id', $productId)
601
+            ->update(['content' => $detail]);
602
+
603
+        return true;
604
+    }
605
+
606
+    /**
607
+     * 处理需要下架的商品(第三方已下架)
608
+     */
609
+    protected function handleOfflineProducts($localSpuIds, $syncedSpuIds)
610
+    {
611
+        $offlineSpuIds = array_diff($localSpuIds, $syncedSpuIds);
612
+
613
+        if (empty($offlineSpuIds)) {
614
+            return;
615
+        }
616
+
617
+        foreach ($offlineSpuIds as $spuId) {
618
+            $this->offlineProductBySpuId($spuId, 'third_party_offline');
619
+        }
620
+
621
+        Log::info("下架第三方已下架商品", ['count' => count($offlineSpuIds), 'spu_ids' => $offlineSpuIds]);
622
+    }
623
+
624
+    /**
625
+     * 根据SPU ID下架商品
626
+     */
627
+    protected function offlineProductBySpuId($spuId, $reason)
628
+    {
629
+        // 查找已上架的商品
630
+        $products = Db::name('store_product')
631
+            ->where('spu_id', $spuId)
632
+            ->select();
633
+
634
+        foreach ($products as $product) {
635
+            // 记录下架前数据
636
+            $beforeData = [
637
+                'product_id' => $product['product_id'],
638
+                'spu_id' => $spuId,
639
+                'title' => $product['store_name'] ?? '',
640
+                'status' => $product['is_show'] ?? 0,
641
+                'price' => $product['price'] ?? 0,
642
+                'record_time' => date('Y-m-d H:i:s')
643
+            ];
644
+
645
+            // 下架商品
646
+            Db::name('store_product')
647
+                ->where('product_id', $product['product_id'])
648
+                ->update([
649
+                    'is_show' => 0,
650
+                    'offline_time' => time(),
651
+                    'offline_reason' => $reason
652
+                ]);
653
+
654
+            // 记录日志
655
+            $this->recordChangeLog(
656
+                $product['product_id'],
657
+                $spuId,
658
+                'third_party_offline',
659
+                $reason,
660
+                $beforeData,
661
+                []
662
+            );
663
+        }
664
+
665
+        // 从douhuomall表中删除
666
+        Db::name('douhuomall')->where('spu_id', $spuId)->delete();
667
+    }
668
+
669
+    /**
670
+     * 记录变动日志
671
+     */
672
+    protected function recordChangeLog($productId, $spuId, $changeType, $reason, $beforeData, $afterData)
673
+    {
674
+        $logData = [
675
+            'product_id' => $productId,
676
+            'spu_id' => $spuId,
677
+            'change_type' => $changeType,
678
+            'change_reason' => $reason,
679
+            'before_data' => json_encode($beforeData, JSON_UNESCAPED_UNICODE),
680
+            'after_data' => json_encode($afterData, JSON_UNESCAPED_UNICODE),
681
+            'change_time' => time(),
682
+            'create_time' => time()
683
+        ];
684
+
685
+        Db::name('product_change_log')->insert($logData);
686
+    }
687
+
688
+    /**
689
+     * 数组排序(从原有add方法中提取)
690
+     */
691
+    protected function arrSort($array, $keys, $sort = SORT_DESC)
692
+    {
693
+        $keysValue = [];
694
+        foreach ($array as $k => $v) {
695
+            $keysValue[$k] = $v[$keys] ?? 0;
696
+        }
697
+        array_multisort($keysValue, $sort, $array);
698
+        return $array;
699
+    }
700
+
701
+    /**
702
+     * 计算成本价
703
+     */
704
+    protected function calculateCostPrice($platPrice)
705
+    {
706
+        return bcadd($platPrice, bcmul($platPrice, '0.03', 4), 2);
707
+    }
708
+
709
+    /**
710
+     * 计算市场价
711
+     */
712
+    protected function calculateMarketPrice($platPrice)
713
+    {
714
+        $costPrice = $this->calculateCostPrice($platPrice);
715
+        return bcadd($costPrice, bcmul($costPrice, '0.2', 4), 2);
716
+    }
717
+
718
+    /**
719
+     * 计算利润率
720
+     */
721
+    protected function calculateProfit($platPrice)
722
+    {
723
+        $costPrice = $this->calculateCostPrice($platPrice);
724
+        $marketPrice = $this->calculateMarketPrice($platPrice);
725
+        return bcdiv($marketPrice, $costPrice, 2);
726
+    }
727
+
728
+    /**
729
+     * 计算毛利
730
+     */
731
+    protected function calculateMerProfit($platPrice)
732
+    {
733
+        $costPrice = $this->calculateCostPrice($platPrice);
734
+        $marketPrice = $this->calculateMarketPrice($platPrice);
735
+        return bcsub($marketPrice, $costPrice, 2);
736
+    }
737
+
738
+    /**
739
+     * 记录错误
740
+     */
741
+    protected function logError($message, \Exception $e)
742
+    {
743
+        $errorInfo = [
744
+            'message' => $message,
745
+            'file' => $e->getFile(),
746
+            'line' => $e->getLine(),
747
+            'error' => $e->getMessage()
748
+        ];
749
+
750
+        Log::error('商品同步错误', $errorInfo);
751
+
752
+        Db::name('log')->insert([
753
+            'data' => json_encode($errorInfo, JSON_UNESCAPED_UNICODE),
754
+            'create_time' => time()
755
+        ]);
756
+    }
757
+}

+ 15 - 0
app/common/enum/douhuomall/DouhuomallEnum.php

@@ -0,0 +1,15 @@
1
+<?php
2
+
3
+namespace app\common\enum\douhuomall;
4
+
5
+use app\common\enum\CommonEnum;
6
+
7
+class DouhuomallEnum extends CommonEnum
8
+{
9
+    // 微唯宝文档中说明:"status": 1,//商品状态(0=下架,1=上架,3=删除)
10
+    const STATUS = [
11
+        'Down' => ['code' => 0, 'name' => '下架'],
12
+        'Up' => ['code' => 1, 'name' => '上架'],
13
+        'Delete' => ['code' => 3, 'name' => '删除'],
14
+    ];
15
+}