Compare commits

...

133 Commits

Author SHA1 Message Date
954de121b8
!232 master
Merge pull request !232 from 赵世界/master
2024-06-19 06:51:06 +00:00
0a01cd95bd
!231 合并
Merge pull request !231 from 赵世界/feat/2024
2024-04-28 07:35:51 +00:00
5b0ffa0c15 日志记录优化 2024-04-28 15:31:47 +08:00
662acf7018
!230 合并
Merge pull request !230 from 赵世界/feat/2024
2024-03-23 07:14:08 +00:00
4e7f17091f feat: 批量修改 2024-03-23 15:13:24 +08:00
f31019bb0a
!229 合并
Merge pull request !229 from 赵世界/feat/2024
2024-03-23 07:10:16 +00:00
12aafe8a94 feat: 批量修改 2024-03-23 15:09:34 +08:00
58c0f5637a
!228 限流修改
Merge pull request !228 from 赵世界/feat/2024
2024-03-23 07:06:03 +00:00
2d47be7291 feat: 批量更新限流修改 2024-03-23 15:05:15 +08:00
b1e715c481
!227 合并
Merge pull request !227 from 赵世界/feat/2024
2024-03-23 06:59:24 +00:00
025a8538eb feat: 修改 2024-03-23 14:58:41 +08:00
393c3acde7
!226 合并
Merge pull request !226 from 赵世界/feat/2024
2024-03-23 06:47:53 +00:00
e9ad57ad46 feat: 修改 2024-03-23 14:46:12 +08:00
e54b1515db
!225 合并
Merge pull request !225 from 赵世界/feat/2024
2024-03-23 05:47:33 +00:00
a6475d7d45 feat: 修改 2024-03-23 13:46:52 +08:00
ba93c0c0cc
!224 合并
Merge pull request !224 from 赵世界/feat/2024
2024-03-23 05:41:34 +00:00
fe97904de0 feat: 修改 2024-03-23 13:40:37 +08:00
9a4c0ba09e
!223 合并
Merge pull request !223 from 赵世界/feat/2024
2024-03-23 05:38:28 +00:00
3057c389ea feat: 修改 2024-03-23 13:37:54 +08:00
a9f5b39ce9
!222 合并
Merge pull request !222 from 赵世界/feat/2024
2024-03-23 03:29:36 +00:00
afb6886640 feat: 修改 2024-03-23 11:28:58 +08:00
b478fd2d65
!221 合并
Merge pull request !221 from 赵世界/feat/2024
2024-03-18 06:13:34 +00:00
10535b560a feat: 目标去化率优化修改 2024-03-18 14:12:57 +08:00
e25b97f8bd
!220 合并
Merge pull request !220 from 赵世界/feat/2024
2024-03-18 05:29:18 +00:00
7b9a40651f feat: 日期筛选修改 2024-03-18 13:28:18 +08:00
5bcbd7f626
!219 合并
Merge pull request !219 from 赵世界/feat/2024
2024-03-18 02:34:49 +00:00
94b674d7d8 feat: 日期筛选优化修改 2024-03-18 10:33:17 +08:00
3f03d292ca feat: 日期筛选优化修改 2024-03-18 10:32:43 +08:00
24337e8427
!218 合并
Merge pull request !218 from 赵世界/feat/2024
2024-03-15 10:46:53 +00:00
ccee406ea9 feat: 优化完成 2024-03-15 18:45:44 +08:00
1d693cef96 feat: 基础完成 2024-03-15 18:45:10 +08:00
088840db5e
!217 合并
Merge pull request !217 from 赵世界/feat/2024
2024-03-15 09:07:41 +00:00
40bf72a4b6 feat: 基础完成 2024-03-15 17:05:22 +08:00
7ac34fcc13 feat: 基础完成 2024-03-15 17:00:25 +08:00
feddef6278 feat: 基础完成 2024-03-15 16:58:25 +08:00
1b02f90047 feat: 销售记录 2024-03-14 20:50:29 +08:00
c3a5f39ae6 feat: 销售记录 2024-03-14 17:13:33 +08:00
ab9e209910 feat: master 商品名称搜索优化 2024-03-14 14:29:23 +08:00
9c5a962c70 feat: master 覆盖 2024-03-13 17:17:47 +08:00
5ea7e26b97
!216 合并
Merge pull request !216 from 赵世界/feat/2024
2024-02-06 09:16:38 +00:00
3fc36352a1 周数据导出 2024-02-06 17:16:09 +08:00
7f1220ceb3
!215 合并
Merge pull request !215 from 赵世界/feat/2024
2024-02-06 09:13:58 +00:00
50db77fc84 周数据导出 2024-02-06 17:13:24 +08:00
6b8c70311b
!214 合并
Merge pull request !214 from 赵世界/feat/2024
2024-02-06 09:06:12 +00:00
4928479795 周数据导出 2024-02-06 17:04:58 +08:00
016f78f7f9
!213 合并
Merge pull request !213 from 赵世界/feat/2024
2024-02-04 09:21:02 +00:00
6753a97806 增加日数据记录 2024-02-04 17:20:06 +08:00
916741cb54
!212 订单重置
Merge pull request !212 from 赵世界/feat/2024
2024-01-29 11:29:49 +00:00
4dd9c85616 订单重置前端打包 2024-01-29 19:27:18 +08:00
fddb5ac67e 订单重置基础完成 2024-01-29 19:23:35 +08:00
2b7fcc998a 订单重置配置 2024-01-27 19:18:44 +08:00
282d055f33
!211 合并
Merge pull request !211 from 赵世界/feat/2024
2024-01-19 09:06:56 +00:00
a96cc3b518 订单倒序 2024-01-19 17:04:56 +08:00
75d940868b
!210 合并
Merge pull request !210 from 赵世界/bugfix/2023-08-25
2023-11-21 08:36:23 +00:00
c6e7f7edb6 库存更新同步修改 2023-11-21 16:35:57 +08:00
7dc861db0d 库存更新同步修改 2023-11-21 16:33:34 +08:00
b242208992 库存更新同步修改 2023-11-21 16:27:14 +08:00
b2b3def740 库存更新优化 2023-11-18 14:35:19 +08:00
0f2e4c3a42
!209 合并
Merge pull request !209 from 赵世界/bugfix/2023-08-25
2023-10-30 06:41:04 +00:00
9c398e154f feat: #10000 上新导入同步成本 2023-10-30 14:40:12 +08:00
63ab1b7745
!208 合并
Merge pull request !208 from 赵世界/bugfix/2023-08-25
2023-10-21 03:25:46 +00:00
f90df4c55c feat: #10000 库存合计更新 2023-10-21 11:25:10 +08:00
93dc936020
!207 合并
Merge pull request !207 from 赵世界/bugfix/2023-08-25
2023-10-21 03:14:49 +00:00
68f0f1ddd5 feat: #10000 库存合计更新 2023-10-21 11:13:59 +08:00
8361a470af
!206 合并
Merge pull request !206 from 赵世界/bugfix/2023-08-25
2023-09-08 07:45:24 +00:00
2bd62e2d09 feat: #10000 更新 2023-09-08 15:40:12 +08:00
11d6565ba4
!205 合并
Merge pull request !205 from 赵世界/bugfix/2023-08-25
2023-09-06 09:04:38 +00:00
496d46ee76 feat: #10000 成本同步 2023-09-06 17:02:48 +08:00
c6092a4fc2
!204 合并
Merge pull request !204 from 赵世界/bugfix/2023-08-25
2023-08-28 10:10:21 +00:00
6380543412 feat: #10000 更新 2023-08-28 18:09:32 +08:00
27e6d329cc
!203 合并
Merge pull request !203 from 赵世界/bugfix/2023-08-25
2023-08-28 09:08:58 +00:00
80fa23f2d3 feat: #10000 更新 2023-08-28 17:07:59 +08:00
bf32c4607c
!202 合并
Merge pull request !202 from 赵世界/bugfix/2023-08-25
2023-08-28 08:59:24 +00:00
11d4d6425b feat: #10000 聚水潭重新授权优化 2023-08-28 16:58:25 +08:00
72d3066fe6
!201 合并
Merge pull request !201 from 赵世界/bugfix/2023-08-25
2023-08-28 08:13:42 +00:00
118b0e31cf feat: #10000 更新 2023-08-28 16:12:45 +08:00
d526129667
!200 合并
Merge pull request !200 from 赵世界/bugfix/2023-08-25
2023-08-28 05:48:44 +00:00
41a809e763 feat: #10000 更新 2023-08-28 13:42:27 +08:00
c8a8baefdb
!199 合并
Merge pull request !199 from 赵世界/bugfix/2023-08-25
2023-08-26 03:08:58 +00:00
9c86453d7c feat: #10000 更新 2023-08-26 11:07:58 +08:00
37c213e58d
!198 合并
Merge pull request !198 from 赵世界/bugfix/2023-08-25
2023-08-25 10:46:29 +00:00
0555b4a536 feat: #10000 更新 2023-08-25 18:38:07 +08:00
c221f8f9ae feat: #100000 更新 2023-08-22 15:26:34 +08:00
7baa81ec71
!195 合并
Merge pull request !195 from 赵世界/develop
2023-08-18 08:57:09 +00:00
c342859b37 feat: #100000 更新 2023-08-18 16:54:04 +08:00
003728f6cc
!194 合并
Merge pull request !194 from 赵世界/develop
2023-08-17 07:25:12 +00:00
31108d613d feat: #100000 更新 2023-08-17 15:24:31 +08:00
625ff396a0
!193 合并
Merge pull request !193 from 赵世界/develop
2023-08-17 06:18:50 +00:00
d7ed54fefe feat: #100000 更新 2023-08-17 14:18:23 +08:00
964a5aa6f3
!192 合并
Merge pull request !192 from 赵世界/develop
2023-08-17 06:17:14 +00:00
c606193665 feat: #100000 更新 2023-08-17 14:01:32 +08:00
b6988e1746
!191 修改
Merge pull request !191 from 赵世界/develop
2023-08-16 07:51:06 +00:00
132364e306 feat: #100000 更新 2023-08-16 15:50:41 +08:00
e60366b848
!190 合并
Merge pull request !190 from 赵世界/develop
2023-08-16 07:41:35 +00:00
d10d924e4f feat: #100000 更新 2023-08-16 15:41:01 +08:00
01b3c68bec
!189 修改
Merge pull request !189 from 赵世界/develop
2023-08-16 07:20:25 +00:00
345ae56fe0 feat: #100000 更新 2023-08-16 15:19:01 +08:00
c286f4467e
!188 合并
Merge pull request !188 from 赵世界/develop
2023-08-16 05:43:39 +00:00
cbba53ca8b feat: #100000 订单打印更新 2023-08-16 13:42:29 +08:00
98f4fcd98b
!187 合并
Merge pull request !187 from 赵世界/develop
2023-08-15 10:02:28 +00:00
6aad8eecc4 feat: #100000 修改 2023-08-15 18:01:48 +08:00
4ade0e6c35 feat: #100000 修改 2023-08-15 17:59:29 +08:00
cc54025b9b feat: #100000 修改 2023-08-15 17:58:56 +08:00
7d4a84ba5c
!186 合并
Merge pull request !186 from 赵世界/develop
2023-08-15 08:34:18 +00:00
ca3a032058 feat: #10000 订单列表优化 2023-08-15 16:33:36 +08:00
ee864ea5f6
!185 合并
Merge pull request !185 from 赵世界/develop
2023-08-11 06:22:53 +00:00
3c0a65b08e feat: #10000 新商品批量导入修改 2023-08-11 14:21:43 +08:00
259d55b578
!184 修改
Merge pull request !184 from 赵世界/develop
2023-08-09 09:48:41 +00:00
2d93ba12f5 feat: #10000 打印测试限制取消 2023-08-09 17:45:20 +08:00
3227d5019e
!183 合并
Merge pull request !183 from 赵世界/develop
2023-08-09 09:12:59 +00:00
b457428d7f feat: #10000 打印测试限制取消 2023-08-09 15:31:29 +08:00
323ed0fdc4
!182 修改
Merge pull request !182 from 赵世界/develop
2023-08-08 10:24:42 +00:00
390c94a6a9 feat: #10000 电子面单账号授权回调修改 2023-08-08 18:24:15 +08:00
06bf1e01b3
!181 修改
Merge pull request !181 from 赵世界/develop
2023-08-08 10:15:50 +00:00
862489e7db feat: #10000 电子面单账号授权回调修改 2023-08-08 18:15:24 +08:00
c7f1aba6fe
!180 修改
Merge pull request !180 from 赵世界/develop
2023-08-08 10:07:14 +00:00
3a092da3e5 feat: #10000 电子面单账号授权回调修改 2023-08-08 18:05:43 +08:00
5caaa00b5b
!179 修改
Merge pull request !179 from 赵世界/develop
2023-08-04 01:32:46 +00:00
d293c200e4 feat: #10000 更新 2023-08-04 09:32:04 +08:00
edc6c77af3
!178 修改
Merge pull request !178 from 赵世界/develop
2023-07-31 06:01:17 +00:00
622304ebf0 feat: #10000 电子面单打印 2023-07-31 14:00:49 +08:00
cc779a52f0
!177 电子面单更新
Merge pull request !177 from 赵世界/develop
2023-07-31 05:31:00 +00:00
e92c460ad0 feat: #10000 电子面单打印 2023-07-31 13:29:05 +08:00
b3f2537768 feat: #10000 打印完成 2023-07-29 17:57:36 +08:00
2ed22b91b5 feat: #10000 订单返回 2023-07-28 19:13:51 +08:00
e35114f842 feat: #10000 订单下单 2023-07-28 15:35:47 +08:00
6d8adb0700 feat: #10000 下单 2023-07-27 21:28:00 +08:00
0cd5af981d feat: #10000 发货地址 2023-07-27 18:30:09 +08:00
880b8717d7 feat: #10000 订单筛选 2023-07-26 16:30:33 +08:00
660fd24ed0 feat: #10000 订单筛选 2023-07-26 13:58:52 +08:00
eb4200a617
!176 修改
Merge pull request !176 from 赵世界/feature
2023-07-18 06:13:08 +00:00
15395085cf feat: #10000 商品编码优化 2023-07-18 14:09:47 +08:00
a2d34b9fc9 feat: #10000 电子面单暂存 2023-07-15 18:18:22 +08:00
240 changed files with 23630 additions and 9940 deletions

View File

@ -20,6 +20,11 @@
6. `php artisan key:generate` 6. `php artisan key:generate`
7. `php artisan update:super_admin_permissions` 更新超级管理员角色权限 7. `php artisan update:super_admin_permissions` 更新超级管理员角色权限
#### 日常更新
1. 更新文件 resources/lang/zh-CN/permission.php
2. `php artisan db:seed --class=MenusTableSeeder` 更新菜单
3. `php artisan db:seed --class=PermissionsTableSeeder` 更新权限
#### 更新权限 #### 更新权限
`php artisan db:seed --class=PermissionsTableSeeder` `php artisan db:seed --class=PermissionsTableSeeder`

View File

@ -0,0 +1,178 @@
<?php
namespace App\Console\Commands;
use App\Models\BusinessOrder;
use App\Models\BusinessOrderItem;
use App\Models\GoodsSku;
use App\Utils\ArrayUtils;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use App\Models\DailySalesReport as DailySalesReportModel;
class DailySalesReport extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'daily:report:sales {S}';
/**
* The console command description.
*
* @var string
*/
protected $description = '每日销量数据记录';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$s = $this->argument('S');
$map = [
'S1' => [
'startTime' => date('Y-m-d 11:00:00'),
'endTime' => date('Y-m-d 12:00:00'),
],
'S2' => [
'startTime' => date('Y-m-d 11:00:00'),
'endTime' => date('Y-m-d 13:30:00'),
],
'S3' => [
'startTime' => date('Y-m-d 11:00:00'),
'endTime' => date('Y-m-d 15:00:00'),
],
'S4' => [
'startTime' => date('Y-m-d 15:00:00'),
'endTime' => date('Y-m-d 16:00:00'),
],
'S5' => [
'startTime' => date('Y-m-d 11:00:00'),
'endTime' => date('Y-m-d 17:30:00'),
],
'S6' => [
'startTime' => date('Y-m-d 11:00:00'),
'endTime' => date('Y-m-d 20:00:00'),
],
'S7' => [
'startTime' => Carbon::yesterday()->format('Y-m-d 11:00:00'),
'endTime' => date('Y-m-d 09:30:00'),
],
];
if (!isset($map[$s])) {
return;
}
$startTime = $map[$s]['startTime'];
$endTime = $map[$s]['endTime'];
$businessOrderIds = BusinessOrder::query()
->where('confirm_at', '>=', Carbon::parse($startTime)->getPreciseTimestamp(3))
->where('confirm_at', '<=', Carbon::parse($endTime)->getPreciseTimestamp(3))
->pluck('id');
$businessOrderIds = $businessOrderIds->chunk(200);
$fields = implode(',', [
'external_sku_id',
'SUM(already_cancel_number) as total_already_cancel_number',
'SUM(goods_number) as total_goods_number',
]);
$data = [];
foreach ($businessOrderIds as $ids) {
$businessOrderItems = BusinessOrderItem::query()
->select(DB::raw($fields))
->whereIn('business_order_id', $ids)
->where('external_sku_id', '<>', '')
->groupBy('external_sku_id')
->get();
if ($businessOrderItems->isEmpty()) {
continue;
}
foreach ($businessOrderItems as $businessOrderItem) {
$arr = explode('_', $businessOrderItem['external_sku_id']);
if (2 !== count($arr)) {
continue;
}
if (!isset($data[$businessOrderItem['external_sku_id']])) {
$data[$businessOrderItem['external_sku_id']] = [
'total_already_cancel_number' => 0,
'total_goods_number' => 0,
];
}
$data[$businessOrderItem['external_sku_id']]['total_already_cancel_number'] += $businessOrderItem['total_already_cancel_number'];
$data[$businessOrderItem['external_sku_id']]['total_goods_number'] += $businessOrderItem['total_goods_number'];
}
}
$date = Carbon::parse($startTime)->format('Y-m-d');
$goodsSkus = GoodsSku::query()
->select(['goods_id', 'external_sku_id', 'name', 'id', 'goal_rate'])
->with(['daily' => function ($query) use ($date) {
$query->where('day', $date);
}])
->whereIn('external_sku_id', array_keys($data))
->get();
$goodsSkus = ArrayUtils::index($goodsSkus->toArray(), 'external_sku_id');
foreach ($data as $externalSkuId => $datum) {
if (!isset($goodsSkus[$externalSkuId])) {
continue;
}
$dailySalesReport = DailySalesReportModel::query()
->where('date', $date)
->where('external_sku_id', $externalSkuId)
->first();
$sVal = $datum['total_goods_number'] - $datum['total_already_cancel_number'];
$sRate = $s . '_rate';
$stock = $goodsSkus[$externalSkuId]['daily']['inventory'] + $goodsSkus[$externalSkuId]['daily']['arrived_today_num'];
$sRateVal = $stock ? bcdiv($sVal, $stock, 4) : 0;
if (is_null($dailySalesReport)) {
DailySalesReportModel::query()->updateOrCreate([
'date' => $date,
'external_sku_id' => $externalSkuId,
], [
'goods_id' => $goodsSkus[$externalSkuId]['goods_id'],
'goods_sku_id' => $goodsSkus[$externalSkuId]['id'],
'name' => $goodsSkus[$externalSkuId]['name'],
'inventory' => $goodsSkus[$externalSkuId]['daily']['inventory'],
'arrived_today_num' => $goodsSkus[$externalSkuId]['daily']['arrived_today_num'],
'sales_num' => $sVal,
'loss_num' => $goodsSkus[$externalSkuId]['daily']['loss_num'],
$s => $sVal,
$sRate => $sRateVal,
'already_cancel_number' => $datum['total_already_cancel_number'],
]);
} else {
$num = 0;
foreach ($map as $key => $val) {
if ($key !== $s) {
$num += $dailySalesReport->$key;
}
}
$dailySalesReport->update([
'inventory' => $goodsSkus[$externalSkuId]['daily']['inventory'],
'arrived_today_num' => $goodsSkus[$externalSkuId]['daily']['arrived_today_num'],
'sales_num' => $num + $sVal,
'loss_num' => $goodsSkus[$externalSkuId]['daily']['loss_num'],
$s => $sVal,
$sRate => $sRateVal,
'already_cancel_number' => $datum['total_already_cancel_number'],
'goal_rate' => $goodsSkus[$externalSkuId]['goal_rate'],
]);
}
}
}
}

View File

@ -0,0 +1,127 @@
<?php
namespace App\Console\Commands;
use App\Models\BusinessOrderItem;
use App\Models\DailyReport;
use App\Models\GoodsSku;
use Carbon\Carbon;
use Illuminate\Console\Command;
class GoodsSkuDailyReport extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'daily:report:goods_sku {date?}';
/**
* The console command description.
*
* @var string
*/
protected $description = '每日商品数据记录';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$date = $this->argument('date');
if (is_null($date)) {
$date = Carbon::yesterday()->format('Y-m-d');
}
$startDateTime = Carbon::parse($date)->startOfDay()->toDateTimeString();
$endDateTime = Carbon::parse($date)->endOfDay()->toDateTimeString();
$orderItems = BusinessOrderItem::query()
->select(['shop_id', 'already_cancel_number', 'external_sku_id', 'goods_amount', 'goods_cost_price', 'goods_number', 'goods_price'])
->where('external_sku_id', '<>', '')
->where('created_at', '>=', $startDateTime)
->where('created_at', '<=', $endDateTime)
->get();
if ($orderItems->isEmpty()) {
return;
}
$externalSkuIds = array_unique(array_column($orderItems->toArray(), 'external_sku_id'));
$goodsSkus = GoodsSku::query()
->select(['id', 'goods_id', 'cost', 'external_sku_id'])
->with([
'goods:id,type_id,brand_id'
])
->where('is_combination', 0)
->whereIn('external_sku_id', $externalSkuIds)
->get()
->pluck(null, 'external_sku_id')
->toArray();
if (empty($goodsSkus)) {
return;
}
$data = [];
foreach ($orderItems as $orderItem) {
if (!isset($goodsSkus[$orderItem->external_sku_id])) {
continue;
}
if (!isset($data[$orderItem->external_sku_id])) {
$data[$orderItem->external_sku_id] = [
'goods_id' => $goodsSkus[$orderItem->external_sku_id]['goods']['id'],
'type_id' => $goodsSkus[$orderItem->external_sku_id]['goods']['type_id'],
'brand_id' => $goodsSkus[$orderItem->external_sku_id]['goods']['brand_id'],
'goods_sku_id' => $goodsSkus[$orderItem->external_sku_id]['id'],
'cost' => $goodsSkus[$orderItem->external_sku_id]['cost'],
'total_goods_price' => 0,
'total_goods_cost_price' => 0,
'total_goods_amount' => 0,
'total_goods_number' => 0,
'total_cancel_number' => 0,
'shop_data' => [],
];
}
$data[$orderItem->external_sku_id]['total_goods_price'] += $orderItem->goods_price;
$data[$orderItem->external_sku_id]['total_goods_cost_price'] += $orderItem->goods_cost_price;
$data[$orderItem->external_sku_id]['total_goods_amount'] += $orderItem->goods_amount;
$data[$orderItem->external_sku_id]['total_goods_number'] += $orderItem->goods_number;
$data[$orderItem->external_sku_id]['total_cancel_number'] += $orderItem->already_cancel_number;
if (!isset($data[$orderItem->external_sku_id]['shop_data'][$orderItem->shop_id])) {
$data[$orderItem->external_sku_id]['shop_data'][$orderItem->shop_id] = [
'total_goods_price' => 0,
'total_goods_cost_price' => 0,
'total_goods_amount' => 0,
'total_goods_number' => 0,
'total_cancel_number' => 0,
];
}
$data[$orderItem->external_sku_id]['shop_data'][$orderItem->shop_id]['total_goods_price'] += $orderItem->goods_price;
$data[$orderItem->external_sku_id]['shop_data'][$orderItem->shop_id]['total_goods_cost_price'] += $orderItem->goods_cost_price;
$data[$orderItem->external_sku_id]['shop_data'][$orderItem->shop_id]['total_goods_amount'] += $orderItem->goods_amount;
$data[$orderItem->external_sku_id]['shop_data'][$orderItem->shop_id]['total_goods_number'] += $orderItem->goods_number;
$data[$orderItem->external_sku_id]['shop_data'][$orderItem->shop_id]['total_cancel_number'] += $orderItem->already_cancel_number;
}
foreach ($data as $externalSkuId => $datum) {
DailyReport::query()->firstOrCreate([
'date' => $date,
'external_sku_id' => $externalSkuId
], $datum);
}
}
}

View File

@ -2,18 +2,24 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Events\CreateLogisticEvent;
use App\Events\StockUpdateEvent; use App\Events\StockUpdateEvent;
use App\Exports\DiffTodayPriceGoodsExport; use App\Exports\DiffTodayPriceGoodsExport;
use App\Jobs\SyncCostToMiaoXuan;
use App\Models\BusinessGoodsSku; use App\Models\BusinessGoodsSku;
use App\Models\BusinessOrder; use App\Models\BusinessOrder;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Models\GoodsType; use App\Models\GoodsType;
use App\Models\Log; use App\Models\Log;
use App\Models\Shop; use App\Models\Shop;
use App\Models\ShopSender;
use App\Models\ShopShip;
use App\Models\TodayPrice; use App\Models\TodayPrice;
use App\Services\Business\BusinessFactory; use App\Services\Business\BusinessFactory;
use App\Services\Business\KuaiTuanTuan\FaceSheet; use App\Services\Business\KuaiTuanTuan\FaceSheet;
use App\Services\Ship\WayBillService;
use App\Utils\DateTimeUtils; use App\Utils\DateTimeUtils;
use Carbon\Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use App\Jobs\BusinessGoodsSkuIncrQuantity; use App\Jobs\BusinessGoodsSkuIncrQuantity;
@ -46,23 +52,7 @@ class Test extends Command
parent::__construct(); parent::__construct();
} }
/**
* Execute the console command.
*
* @return mixed
*/
public function handle() public function handle()
{ {
$shops = Shop::query()->where('plat_id', Shop::$PLAT_KTT)->where('status', Shop::$STATUS_AUTHORIZED)->where('id', 6)->get();
foreach ($shops as $shop) {
$faceSheet = new FaceSheet();
$faceSheet->setShop($shop);
var_dump($faceSheet->searchWayBill());
}
}
public function getAuthUrl()
{
return "https://wb.pinduoduo.com/logistics/auth?client_id=24f25877aca447c5830a6aa896301d5e&redirect_uri=http://erp.chutang66.com/pdd/ship";
} }
} }

View File

@ -0,0 +1,49 @@
<?php
namespace App\Console\Commands;
use App\Models\GoodsSku;
use Illuminate\Console\Command;
class UpdateGoodsSkuName extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'update:goods_skus:name';
/**
* The console command description.
*
* @var string
*/
protected $description = '更新goods_sku的完整名称';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
GoodsSku::query()
->where('name', '')
->where('is_combination', 0)
->chunk(500, static function ($skus) {
foreach ($skus as $sku) {
$sku->name = $sku->goods->title . $sku->title;
$sku->save();
}
});
}
}

View File

@ -2,6 +2,8 @@
namespace App\Console; namespace App\Console;
use App\Console\Commands\DailySalesReport;
use App\Console\Commands\GoodsSkuDailyReport;
use App\Console\Commands\Inventory; use App\Console\Commands\Inventory;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@ -29,8 +31,19 @@ class Kernel extends ConsoleKernel
{ {
// 服务器/etc/crontab添加cron入口 // 服务器/etc/crontab添加cron入口
// * * * * * cd /home/wwwroot/erp.chutang66.com && php artisan schedule:run >> /dev/null 2>&1 // * * * * * cd /home/wwwroot/erp.chutang66.com && php artisan schedule:run >> /dev/null 2>&1
$schedule->command(Inventory::class)->dailyAt('07:00');
$schedule->command(KttOrderQuery::class)->everyMinute(); $schedule->command(KttOrderQuery::class)->everyMinute();
$schedule->command(GoodsSkuDailyReport::class)->dailyAt('06:00');
$schedule->command(Inventory::class)->dailyAt('07:00');
$schedule->command(DailySalesReport::class, ['S1'])->dailyAt('12:00');
$schedule->command(DailySalesReport::class, ['S2'])->dailyAt('13:30');
$schedule->command(DailySalesReport::class, ['S3'])->dailyAt('15:00');
$schedule->command(DailySalesReport::class, ['S4'])->dailyAt('16:00');
$schedule->command(DailySalesReport::class, ['S5'])->dailyAt('17:30');
$schedule->command(DailySalesReport::class, ['S6'])->dailyAt('20:00');
$schedule->command(DailySalesReport::class, ['S7'])->dailyAt('09:30');
$schedule->command(DeleteKttQuery::class)->daily(); $schedule->command(DeleteKttQuery::class)->daily();
} }

View File

@ -0,0 +1,42 @@
<?php
namespace App\Events;
use App\Models\GoodsSku;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class BatchStockUpdateEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $goodsSkus;
public $combinationGoodsUpdate;
/**
* Create a new event instance.
*
* @param $goodsSkuIds
* @param bool $combinationGoodsUpdate
*/
public function __construct($goodsSkuIds, bool $combinationGoodsUpdate = true)
{
$this->goodsSkus = GoodsSku::query()->whereIn('id', $goodsSkuIds)->get();
$this->combinationGoodsUpdate = $combinationGoodsUpdate;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class BusinessOrderCancelEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $shopId;
public $orderSn;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($shopId, $orderSn)
{
$this->shopId = $shopId;
$this->orderSn = $orderSn;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -2,7 +2,9 @@
namespace App\Events; namespace App\Events;
use App\Models\DailyStockRecord;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Utils\DateTimeUtils;
use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PresenceChannel;
@ -16,39 +18,51 @@ class BusinessOrdersUpdate
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $businessGoodSku;
public $num; public $num;
public $businessGoods;
public $goodsSku; public $goodsSku;
public $goodsSkus; public $combinationGoodsUpdate = true;
public $combinationGoodsUpdate;
/** /**
* Create a new event instance. * Create a new event instance.
* *
* @return void * @return void
*/ */
public function __construct($item, $num) public function __construct($businessGoodSku, $num)
{ {
$this->combinationGoodsUpdate = false; $this->businessGoodSku = $businessGoodSku->toArray();
$this->businessGoods = $item->toArray();
$this->num = $num; $this->num = $num;
$this->updateStock(); $this->updateStock();
} }
private function updateStock() private function updateStock()
{ {
try {
$this->goodsSku = GoodsSku::query() $this->goodsSku = GoodsSku::query()
->where('external_sku_id', $this->businessGoods['external_sku_id']) ->where('external_sku_id', $this->businessGoodSku['external_sku_id'])
->first(); ->first();
} catch (\Exception $e) { if (is_null($this->goodsSku)) {
Log::error('事件库存更新失败: ' . $e->getMessage());
return false; return false;
} }
if ($this->goodsSku) { $stock = $this->goodsSku->stock + $this->num;
$this->goodsSku->stock += $this->num;
$this->goodsSku->save(); if (0 >= $stock) {
$this->goodsSku->status = GoodsSku::$STATUS_DOWN;
} else {
$this->goodsSku->status = GoodsSku::$STATUS_ON_SALE;
} }
// 今日到货 + 1T 大于20,且当前剩余库存小于4时 直接下架
$arrivedTodayNum = DailyStockRecord::query()
->where('day', DateTimeUtils::getToday())
->where('sku_id', $this->goodsSku->id)
->value('arrived_today_num');
if (20 < $arrivedTodayNum + $this->goodsSku->yesterday_num && 4 > $stock) {
$this->goodsSku->status = GoodsSku::$STATUS_DOWN;
$stock = 0;
}
$this->goodsSku->stock = $stock;
$this->goodsSku->save();
} }
/** /**

View File

@ -0,0 +1,42 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class CreateLogisticEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $shopId;
public $orderSn;
public $waybillNo;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($shopId, $orderSn, $waybillNo)
{
$this->shopId = $shopId;
$this->orderSn = $orderSn;
$this->waybillNo = $waybillNo;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -2,7 +2,9 @@
namespace App\Events; namespace App\Events;
use App\Models\DailyStockRecord;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Utils\DateTimeUtils;
use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PresenceChannel;
@ -16,28 +18,38 @@ class StockUpdateEvent
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $goodsSku; public $goodsSku;
public $goodsSkus; public $combinationGoodsUpdate = true;
public $isBatch;
public $combinationGoodsUpdate;
/** /**
* Create a new event instance. * Create a new event instance.
* *
* @param $data array|object * @param $goodsSku
*
* @return void
*/ */
public function __construct($data, $isBatch = 0, $combinationGoodsUpdate = false) public function __construct($goodsSku)
{ {
$this->isBatch = $isBatch; $this->goodsSku = $goodsSku;
$this->combinationGoodsUpdate = $combinationGoodsUpdate; $this->checkStatusAndStock($goodsSku);
if (is_array($data)) {
// ids集合
$this->goodsSkus = GoodsSku::query()->whereIn('id', $data)->get();
} else {
// GoodsSku Elo模型对象
$this->goodsSku = $data;
} }
private function checkStatusAndStock($goodsSku)
{
$stock = $goodsSku->stock;
if (0 >= $goodsSku->stock) {
$status = GoodsSku::$STATUS_DOWN;
} else {
$status = GoodsSku::$STATUS_ON_SALE;
}
$arrivedTodayNum = DailyStockRecord::query()
->where('day', DateTimeUtils::getToday())
->where('sku_id', $goodsSku->id)
->value('arrived_today_num');
if (20 < $arrivedTodayNum + $goodsSku->yesterday_num && 4 > $goodsSku->stock) {
$status = GoodsSku::$STATUS_DOWN;
$stock = 0;
}
$goodsSku->status = $status;
$goodsSku->stock = $stock;
$goodsSku->save();
} }
/** /**

View File

@ -0,0 +1,111 @@
<?php
namespace App\Exports;
use App\Models\DailyReport;
use App\Models\Shop;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Illuminate\Support\Collection;
class WeekDataExport implements FromCollection, ShouldAutoSize
{
private $startDate;
private $endDate;
private $data;
public function __construct($startDate, $endDate)
{
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->data = $this->createData();
}
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return new Collection($this->data);
}
private function createData()
{
$headTitle = [
'商品名称',
'品类',
'规格',
'品牌',
'商品编码',
'成本',
'销售数量',
];
$shops = Shop::query()
->whereNotIn('id', [7, 9, 10, 11, 17, 18])
->orderBy('id')
->pluck('name', 'id')
->toArray();
$headTitle = array_merge($headTitle, array_values($shops));
$dailyReports = DailyReport::query()
->with([
'goods:id,title',
'goodsType:id,name',
'goodsSku:id,title',
'goodsBrand:id,name',
])
->where('date', '>=', $this->startDate)
->where('date', '<=', $this->endDate)
->get();
if ($dailyReports->isEmpty()) {
return [$headTitle];
}
$arr = [];
foreach ($dailyReports as $item) {
$number = $item->total_goods_number - $item->total_cancel_number;
if (!isset($arr[$item->external_sku_id])) {
$arr[$item->external_sku_id] = [
'goodsTitle' => $item->goods->title,
'goodsTypeName' => $item->goodsType->name,
'goodsSkuTitle' => $item->goodsSku->title,
'goodsBrandName' => $item->goodsBrand->name,
'external_sku_id' => $item->external_sku_id,
'cost' => [],
'number' => [],
'shops' => []
];
}
$arr[$item->external_sku_id]['cost'][] = $item->cost;
$arr[$item->external_sku_id]['number'][] = $number;
foreach ($item->shop_data as $shopId => $shopData) {
$number = $shopData['total_goods_number'] - $shopData['total_cancel_number'];
if (!isset($arr[$item->external_sku_id]['shops'][$shopId])) {
$arr[$item->external_sku_id]['shops'][$shopId] = 0;
}
$arr[$item->external_sku_id]['shops'][$shopId] += $number;
}
}
$bodyData = [];
foreach ($arr as $item) {
$cost = array_sum($item['cost']) / count($item['cost']);
$shopData = [];
foreach ($shops as $shopId => $shopName) {
$shopData[] = $item['shops'][$shopId] ?? 0;
}
$data = [
$item['goodsTitle'],
$item['goodsTypeName'],
$item['goodsSkuTitle'],
$item['goodsBrandName'],
$item['external_sku_id'],
$cost,
array_sum($item['number']),
];
$bodyData[] = array_merge($data, $shopData);
}
return [$headTitle, $bodyData];
}
}

View File

@ -2,8 +2,17 @@
namespace App\Filters; namespace App\Filters;
use App\Models\BusinessOrderItem;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
class BusinessOrderFilter extends Filters class BusinessOrderFilter extends Filters
{ {
protected function ids($value)
{
return $this->builder->whereIn('id', $value);
}
protected function participateNo($value) protected function participateNo($value)
{ {
return $this->builder->where('participate_no', trim($value)); return $this->builder->where('participate_no', trim($value));
@ -14,6 +23,14 @@ class BusinessOrderFilter extends Filters
return $this->builder->where('supply_participate_no', trim($value)); return $this->builder->where('supply_participate_no', trim($value));
} }
protected function pno($value)
{
return $this->builder->where(function (Builder $builder) use ($value) {
$builder->where('participate_no', $value)
->orWhere('supply_participate_no', $value);
});
}
protected function shopId($value) protected function shopId($value)
{ {
return $this->builder->where('shop_id', $value); return $this->builder->where('shop_id', $value);
@ -43,4 +60,39 @@ class BusinessOrderFilter extends Filters
{ {
return $this->builder->where('after_sales_status', $value); return $this->builder->where('after_sales_status', $value);
} }
protected function confirmAtStart($value)
{
$start = Carbon::parse($value)->timestamp;
$start *= 1000;
return $this->builder->where('confirm_at', '>=', $start);
}
protected function confirmAtEnd($value)
{
$end = Carbon::parse($value)->timestamp;
$end *= 1000;
return $this->builder->where('confirm_at', '<=', $end);
}
protected function goodsSkuNum($value)
{
if (1 === $value) {
return $this->builder->where('goods_sku_num', '=', 1);
}
if (2 === $value) {
return $this->builder->where('goods_sku_num', '<=', 5)
->where('goods_sku_num', '>=', 2);
}
if (6 === $value) {
return $this->builder->where('goods_sku_num', '>=', 6);
}
}
protected function printStatus($value)
{
return $this->builder->where('print_status', $value);
}
} }

View File

@ -4,11 +4,6 @@ namespace App\Filters;
class GoodsFilter extends Filters class GoodsFilter extends Filters
{ {
protected function goodsTitle($value)
{
return $this->builder->where('title', 'like', "%$value%");
}
protected function typeId($value) protected function typeId($value)
{ {
if($value){ if($value){

View File

@ -23,4 +23,9 @@ class GoodsSkuFilter extends Filters
{ {
return $this->builder->where('external_sku_id', $value); return $this->builder->where('external_sku_id', $value);
} }
protected function isCombination($value)
{
return $this->builder->where('is_combination', $value);
}
} }

View File

@ -5,87 +5,111 @@ namespace App\Http\Controllers\Business;
use App\Exports\OrderBlankExport; use App\Exports\OrderBlankExport;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\BusinessOrder; use App\Models\BusinessOrder;
use App\Models\BusinessOrderItem;
use App\Models\GoodsSku;
use App\Models\Shop; use App\Models\Shop;
use App\Services\Ship\WayBillService;
use App\Utils\DateTimeUtils; use App\Utils\DateTimeUtils;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Resources\BusinessOrderResource; use App\Http\Resources\BusinessOrderResource;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel; use Maatwebsite\Excel\Facades\Excel;
class BusinessOrderController extends Controller class BusinessOrderController extends Controller
{ {
public function index(Request $request) public function index(Request $request)
{ {
$businessOrders = BusinessOrder::query() $shopIds = Shop::query()
->where('shop_id', '<>', 8) ->where('plat_id', Shop::$PLAT_KTT)
->pluck('id');
$builder = BusinessOrder::query()
->with([ ->with([
'shop:id,name', 'shop:id,name',
'items:id,business_order_id,goods_name,goods_number,external_sku_id' 'items:id,business_order_id,goods_name,goods_number,external_sku_id'
]) ])
->orderByDesc('confirm_at') ->whereIn('shop_id', $shopIds)
->filter() ->filter();
$externalSkuIds = $request->get('external_sku_ids');
if ($externalSkuIds) {
$ids = BusinessOrderItem::query()->whereIn('external_sku_id', $externalSkuIds)->pluck('business_order_id');
$builder->whereIn('id', $ids);
}
$businessOrders = $builder->orderByDesc('confirm_at')
->paginate($request->get('per_page')); ->paginate($request->get('per_page'));
return BusinessOrderResource::collection($businessOrders); return BusinessOrderResource::collection($businessOrders);
} }
private function getOrderIdsWitmGoodsNum($goodsSkuNum, $orders)
{
$map = [
1 => [
'min' => 1,
'max' => 1
],
2 => [
'min' => 2,
'max' => 5
],
6 => [
'min' => 6,
'max' => 999999
],
];
$numMap = $map[$goodsSkuNum];
// 获取订单商品编码
$externalSkuIds = [];
foreach ($orders as $order) {
foreach ($order->items as $item) {
if ($item['external_sku_id']) {
$externalSkuIds [] = $item['external_sku_id'];
}
}
}
$goodsSkus = GoodsSku::query()
->with('combinationGoods')
->whereIn('external_sku_id', $externalSkuIds)
->get('external_sku_id');
$goodsSkuItems = [];
foreach ($goodsSkus as $goodsSku) {
$goodsSkuItems[$goodsSku['external_sku_id']] = $goodsSku->combinationGoods->count() ?: 1;
}
$ids = [];
foreach ($orders as $order) {
$itemNum = 0;
foreach ($order->items as $item) {
if (0 === $item['cancel_status']) {
$itemNum = isset($goodsSkuItems[$item['external_sku_id']]) ? $itemNum + $goodsSkuItems[$item['external_sku_id']] : $itemNum + 1;
}
}
if ($itemNum >= $numMap['min'] && $itemNum <= $numMap['max']) {
$ids[] = $order['id'];
}
}
return $ids;
}
public function exportOrderBlank(Request $request) public function exportOrderBlank(Request $request)
{ {
$shopId = $request->get('shop_id'); $orderIds = $request->get('order_ids');
$startNo = $request->get('start_no');
$endNo = $request->get('end_no');
$isSupplier = $request->get('is_supplier');
$field = 'participate_no';
if ($isSupplier) {
$field = 'supply_participate_no';
}
$startTime = BusinessOrder::query()
->where('shop_id', $shopId)
->where('is_supplier', $isSupplier)
->where($field, $startNo)
->orderByDesc('id')
->value('confirm_at');
if (empty($startTime)) {
exit('开始跟团号订单未查询到或正在同步中,请稍后再次获取');
}
$startTime = DateTimeUtils::getMicroTime($startTime);
$endTime = BusinessOrder::query()
->where('shop_id', $shopId)
->where('is_supplier', $isSupplier)
->where($field, $endNo)
->orderByDesc('id')
->value('confirm_at');
if (empty($endTime)) {
exit('结束跟团号订单未查询到或正在同步中,请稍后再次获取');
}
$endTime = DateTimeUtils::getMicroTime($endTime);
if ($startTime > $endTime) {
exit('开始跟团号订单 成交时间 大于 结束跟团号订单时间,请查验后再试!');
}
$orders = BusinessOrder::query() $orders = BusinessOrder::query()
->with([ ->with([
'items:id,business_order_id,external_sku_id,goods_number,goods_name,already_cancel_number', 'items:id,business_order_id,external_sku_id,goods_number,goods_name,already_cancel_number',
'items.goodsSkuLocation:id,external_sku_id,location,goods_name' 'items.goodsSkuLocation:id,external_sku_id,location,goods_name'
]) ])
->where('shop_id', $shopId) ->whereIn('id', $orderIds)
->where('confirm_at', '>=', $startTime) ->get(['id']);
->where('confirm_at', '<=', $endTime)
->where('after_sales_status', 0)
->where('cancel_status', 0)
->where('is_supplier', $isSupplier)
->orderByDesc('confirm_at')
->get(['id', $field]);
$distribution = []; $distribution = [];
$no = []; $no = [];
foreach ($orders as $key => $order) { foreach ($orders as $key => $order) {
$index = $key + 1; $index = $key + 1;
$no[] = 'P' . $index . '(' . $order->$field . ')'; $no[] = 'P' . $index;
foreach ($order->items as $item) { foreach ($order->items as $item) {
$item = $item->toArray();
$num = $item['goods_number'] - $item['already_cancel_number']; $num = $item['goods_number'] - $item['already_cancel_number'];
if (empty($item['external_sku_id']) || empty($num)) {
continue;
}
$local = $item['goods_sku_location'] ? $item['goods_sku_location']['location'] : '无'; $local = $item['goods_sku_location'] ? $item['goods_sku_location']['location'] : '无';
$index = $key + 1; $index = $key + 1;
$index = "P{$index}*{$num}"; $index = "P{$index}*{$num}";
@ -105,10 +129,10 @@ class BusinessOrderController extends Controller
} }
} }
} }
$shopName = Shop::query()->where('id', $shopId)->value('name'); $fileName = time();
ob_end_clean(); ob_end_clean();
return Excel::download(new OrderBlankExport($shopName, $no, $distribution), $shopName . date('Y-m-d H:i:s') . '.xlsx'); return Excel::download(new OrderBlankExport($fileName, $no, $distribution), $fileName . date('Y-m-d H:i:s') . '.xlsx');
} }
public function groupActivity(Request $request, $shopId) public function groupActivity(Request $request, $shopId)
@ -123,4 +147,94 @@ class BusinessOrderController extends Controller
->get(['activity_title', 'activity_no']) ->get(['activity_title', 'activity_no'])
->toArray(); ->toArray();
} }
public function print(Request $request)
{
$shopIds = Shop::query()
->where('plat_id', Shop::$PLAT_KTT)
->pluck('id');
$builder = BusinessOrder::query()
->with('items')
->whereIn('shop_id', $shopIds)
->filter();
$externalSkuIds = $request->get('external_sku_ids');
if ($externalSkuIds) {
$ids = BusinessOrderItem::query()->whereIn('external_sku_id', $externalSkuIds)->pluck('business_order_id');
$builder->whereIn('id', $ids);
}
if ($ids = $request->input('ids')) {
$builder->whereIn('id', $ids);
}
$businessOrders = $builder->get();
$waybill = new WayBillService();
$waybill->setOrders($businessOrders);
$contents = $waybill->getContents();
// 待打印数据
[$documents, $orderIds] = $waybill->getDocumentsAndOrderIds($contents);
return response([
'documents' => $this->combinationPrintDocuments($documents),
'order_ids' => implode(',', $orderIds),
]);
}
private function combinationPrintDocuments($documents)
{
$documentData = [
'data' => [
'height' => 240,
'list' => [
[
'fontSize' => 28,
// 'height' => 60, // 外部的height/此处的height
'left' => 5,
'text' => '', // 备注
'top' => 10,
'width' => 550
]
],
'waterdata' => [
'text' => ''
],
'width' => 560
],
'templateURL' => "http://pinduoduoimg.yangkeduo.com/logistics/2019-07-14/5d7e8b5969d954539fcfba3268bbeb3a.xml" // 自定义区域模板
];
$data = [];
foreach ($documents as &$document) {
$documentData['data']['list'][0]['text'] = $document['participate_no'] ? '[跟团号: ' . $document['participate_no'] . '] ' : '';
$documentID = $document['documentID'];
$count = 0;
foreach ($document['items'] as $item) {
$count += $item['count'];
$documentData['data']['list'][0]['text'] .= $item['name'] . ' ' . $item['count'] . '件;';
}
$documentData['data']['list'][0]['text'] .= ' 总计: ' . $count . '件';
if ($document['note']) {
$documentData['data']['list'][0]['text'] .= ' 备注:' . $document['note'];
}
unset($document['documentID'], $document['items'], $document['order_id'], $document['participate_no'], $document['note']);
$data[] = [
'documentID' => $documentID,
'contents' => [
$document,
$documentData
]
];
}
return $data;
}
public function printSuccess(Request $request)
{
$orderIds = $request->input('order_ids');
$orderIds = explode(',', $orderIds);
BusinessOrder::query()
->where('id', $orderIds)
->increment('print_status');
return response(['message' => 'success']);
}
} }

View File

@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\DataCenter;
use App\Http\Controllers\Controller;
use App\Http\Resources\DailySalesReportResource;
use App\Models\DailySalesReport;
use App\Utils\FormatUtils;
use Illuminate\Http\Request;
class DataCenterController extends Controller
{
public function salesReport(Request $request)
{
$sort = $request->get('sort', 'sales_num');
$order = $request->get('sortOrder', 'DESC');
if (empty($request->get('date'))) {
$request->offsetSet('date', date('Y-m-d'));
}
$dailySalesReports = DailySalesReport::query()
->filter()
->orderBy($sort, $order)
->paginate($request->get('per_page'));
foreach ($dailySalesReports as &$dailySalesReport) {
$dailySalesReport['stock'] = $dailySalesReport['inventory'] + $dailySalesReport['arrived_today_num'];
$dailySalesReport['goal_rate'] = FormatUtils::getPercent($dailySalesReport['goal_rate'], 1);
$dailySalesReport['S1_rate'] = FormatUtils::getPercent($dailySalesReport['S1_rate'], 1);
$dailySalesReport['S2_rate'] = FormatUtils::getPercent($dailySalesReport['S2_rate'], 1);
$dailySalesReport['S3_rate'] = FormatUtils::getPercent($dailySalesReport['S3_rate'], 1);
$dailySalesReport['S4_rate'] = FormatUtils::getPercent($dailySalesReport['S4_rate'], 1);
$dailySalesReport['S5_rate'] = FormatUtils::getPercent($dailySalesReport['S5_rate'], 1);
$dailySalesReport['S6_rate'] = FormatUtils::getPercent($dailySalesReport['S6_rate'], 1);
$dailySalesReport['S7_rate'] = FormatUtils::getPercent($dailySalesReport['S7_rate'], 1);
}
return DailySalesReportResource::collection($dailySalesReports);
}
}

View File

@ -2,7 +2,6 @@
namespace App\Http\Controllers\Goods; namespace App\Http\Controllers\Goods;
use App\Events\StockUpdateEvent;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Resources\GoodsSkuResource; use App\Http\Resources\GoodsSkuResource;
use App\Imports\CombinationGoodsImport; use App\Imports\CombinationGoodsImport;
@ -141,7 +140,6 @@ class GoodsCombinationController extends Controller
CombinationGood::query()->create(['goods_sku_id' => $sku->id, 'item_id' => $item['item_id'], 'item_num' => $item['item_num']]); CombinationGood::query()->create(['goods_sku_id' => $sku->id, 'item_id' => $item['item_id'], 'item_num' => $item['item_num']]);
} }
DB::commit(); DB::commit();
// event(new StockUpdateEvent($sku, 0, true));
} catch (\Exception $exception) { } catch (\Exception $exception) {
DB::rollBack(); DB::rollBack();
$this->res = [ $this->res = [

View File

@ -61,6 +61,7 @@ class GoodsController extends Controller
$item['stock'] = $item['num']; $item['stock'] = $item['num'];
$item['reference_price'] = $item['cost'] * 1.5; $item['reference_price'] = $item['cost'] * 1.5;
$item['external_sku_id'] = $goods->goods_code . '_' . $item['sku_code']; $item['external_sku_id'] = $goods->goods_code . '_' . $item['sku_code'];
$item['name'] = $goods->title . $item['title'];
$goodsSkus[] = $item; $goodsSkus[] = $item;
} }
$collection = $goods->skus()->createMany($goodsSkus)->toArray(); $collection = $goods->skus()->createMany($goodsSkus)->toArray();

View File

@ -2,19 +2,24 @@
namespace App\Http\Controllers\Goods; namespace App\Http\Controllers\Goods;
use App\Events\BatchStockUpdateEvent;
use App\Events\StockUpdateEvent; use App\Events\StockUpdateEvent;
use App\Exports\GoodsSkusExport; use App\Exports\GoodsSkusExport;
use App\Exports\WeekDataExport;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\GoodsRequest; use App\Http\Requests\GoodsRequest;
use App\Http\Requests\GoodsSkuRequest; use App\Http\Requests\GoodsSkuRequest;
use App\Imports\InventoryImport; use App\Imports\InventoryImport;
use App\Imports\NewSetImport; use App\Imports\NewSetImport;
use App\Models\BusinessOrderItem; use App\Models\BusinessOrderItem;
use App\Models\DailySalesReport;
use App\Models\DeveloperConfig;
use App\Models\Goods; use App\Models\Goods;
use App\Models\Log; use App\Models\Log;
use App\Models\Log as LogModel; use App\Models\Log as LogModel;
use App\Utils\ArrayUtils; use App\Utils\ArrayUtils;
use App\Utils\DateTimeUtils; use App\Utils\DateTimeUtils;
use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Http\Resources\GoodsSkuResource; use App\Http\Resources\GoodsSkuResource;
@ -40,46 +45,85 @@ class GoodsSkusController extends Controller
public function index(Request $request) public function index(Request $request)
{ {
$fields = implode(',', [
'shop_id',
'external_sku_id',
'SUM(goods_number) - SUM(already_cancel_number) as number',
]);
$orderRestTime = DeveloperConfig::query()
->where('key', DeveloperConfig::$ORDER_RESET_TIME)
->value('value');
if (is_null($orderRestTime)) {
$orderRestTime = date('Y-m-d 07:00:00');
}
$businessOrderItems = BusinessOrderItem::query()
->select(DB::raw($fields))
->with([
'shop:id,name',
'goodsSku:id,external_sku_id'
])
->where('created_at', '>', $orderRestTime)
->where('external_sku_id', '<>', '')
->groupBy(['shop_id', 'external_sku_id'])
->orderByDesc('number')
->get()
->toArray();
$ids = $externals = [];
foreach ($businessOrderItems as $businessOrderItem) {
if (is_null($businessOrderItem['goods_sku'])) {
continue;
}
$id = $businessOrderItem['goods_sku']['id'];
if (isset($ids[$id])) {
$ids[$id] += (int)$businessOrderItem['number'];
} else {
$ids[$id] = (int)$businessOrderItem['number'];
}
$externals[$businessOrderItem['external_sku_id']][] = $businessOrderItem;
}
arsort($ids);
$builder = GoodsSku::query(); $builder = GoodsSku::query();
$this->preparQueryGoodsSkus($request, $builder); $this->preparQueryGoodsSkus($request, $builder);
$day = DateTimeUtils::getToday(); $day = DateTimeUtils::getToday();
$goodsSkus = $builder->filter() $goodsSkus = (clone $builder)->filter()
->with(['goods' => function ($query) { ->where('is_combination', 0)
->orderByDesc('stock')
->pluck('stock', 'id')
->toArray();
$finalIds = [];
foreach ($ids as $id => $number) {
if (isset($goodsSkus[$id])) {
$finalIds[] = $id;
unset($goodsSkus[$id]);
}
}
$finalIds = array_merge($finalIds, array_keys($goodsSkus));
$idField = implode(',', $finalIds);
$goodsSkus = (clone $builder)->with(['goods' => function ($query) {
$query->with(['type:id,name', 'brand:id,name']); $query->with(['type:id,name', 'brand:id,name']);
}]) }])
->with(['daily' => function ($query) use ($day) { ->with(['daily' => function ($query) use ($day) {
$query->where('day', $day); $query->where('day', $day);
}]) }])
->where('is_combination', 0) ->whereIn('id', $finalIds)
->orderBy('stock', 'desc') ->orderByRaw("FIELD(id, {$idField})")
->paginate($request->get('per_page')); ->paginate($request->get('per_page'));
$fields = implode(',', [
'shop_id',
'external_sku_id',
'sum(goods_number) as number',
'sum(already_cancel_number) as cancel_number',
]);
$rolesName = $request->user()->getRoleNames()->toArray(); $rolesName = $request->user()->getRoleNames()->toArray();
foreach ($goodsSkus as &$sku) { foreach ($goodsSkus as &$sku) {
$lastInventoryTime = $sku['daily']['inventory_time']; $lastInventoryTime = $sku['daily']['inventory_time'] ?: date('Y-m-d 07:00:00');
$orderDetail = BusinessOrderItem::query() if (isset($externals[$sku['external_sku_id']])) {
->select(DB::raw($fields)) $sku['order_detail'] = $externals[$sku['external_sku_id']];
->with(['shop:id,name']) $sku['order_goods_num'] = array_sum(array_column($externals[$sku['external_sku_id']], 'number'));
->where('external_sku_id', $sku['external_sku_id']) } else {
->when($lastInventoryTime, function ($query) use ($lastInventoryTime) { $sku['order_detail'] = [];
$query->where('created_at', '>', $lastInventoryTime); $sku['order_goods_num'] = 0;
})
->groupBy(['shop_id', 'external_sku_id'])
->get()
->toArray();
$addOrderGoodsNum = $reduceOrderGoodsNum = 0;
if ($orderDetail) {
$addOrderGoodsNum = array_sum(array_column($orderDetail, 'number'));
$reduceOrderGoodsNum = array_sum(array_column($orderDetail, 'cancel_number'));
} }
$sku['order_goods_num'] -= $sku['daily']['reissue_num'];
$sku['inventory_time'] = $lastInventoryTime; $sku['inventory_time'] = $lastInventoryTime;
$sku['order_goods_num'] = $addOrderGoodsNum - $reduceOrderGoodsNum;
$sku['order_detail'] = $orderDetail;
if ('销售' === $rolesName[0]) { if ('销售' === $rolesName[0]) {
$sku['cost'] = 0; $sku['cost'] = 0;
} }
@ -98,10 +142,13 @@ class GoodsSkusController extends Controller
->toArray(); ->toArray();
$builder->whereIn('id', $skuIds); $builder->whereIn('id', $skuIds);
} }
if ($request->get('goods_title') || $request->get('type_id') || $request->get('brand_id')) { if ($request->get('type_id') || $request->get('brand_id')) {
$goodsIds = Goods::query()->filter()->pluck('id')->toArray(); $goodsIds = Goods::query()->filter()->pluck('id')->toArray();
$builder->whereIn('goods_id', $goodsIds); $builder->whereIn('goods_id', $goodsIds);
} }
if ($request->get('goods_title')) {
$builder->where('name', 'like', '%' . $request->goods_title . '%');
}
} }
public function show($id) public function show($id)
@ -143,6 +190,7 @@ class GoodsSkusController extends Controller
$this->setBeforeUpdateForLog($sku->toArray()); $this->setBeforeUpdateForLog($sku->toArray());
$skuInfo = $request->sku; $skuInfo = $request->sku;
$skuInfo['external_sku_id'] = $request->goods['goods_code'] . '_' . $request->sku['sku_code']; $skuInfo['external_sku_id'] = $request->goods['goods_code'] . '_' . $request->sku['sku_code'];
$skuInfo['name'] = $request->goods['title'] . $request->sku['title'];
$sku->update($skuInfo); $sku->update($skuInfo);
$this->setAfterUpdateForLog($sku->toArray()); $this->setAfterUpdateForLog($sku->toArray());
$this->addLog($id, 'update'); $this->addLog($id, 'update');
@ -234,7 +282,8 @@ class GoodsSkusController extends Controller
$log = new LogModel(); $log = new LogModel();
$log->batchInsert($logs); $log->batchInsert($logs);
DB::commit(); DB::commit();
event(new StockUpdateEvent(array_column($request->skus, 'id'))); // 批量更新
event(new BatchStockUpdateEvent(array_column($request->skus, 'id')));
} catch (\Exception $exception) { } catch (\Exception $exception) {
DB::rollBack(); DB::rollBack();
$this->res = [ $this->res = [
@ -389,7 +438,7 @@ class GoodsSkusController extends Controller
$rules = [ $rules = [
'updateField' => [ 'updateField' => [
'required', 'required',
Rule::in(['reference_price', 'reserve', 'loss_num', 'status']) Rule::in(['reference_price', 'reserve', 'loss_num', 'status', 'goal_rate'])
], ],
'reference_price' => [ 'reference_price' => [
'sometimes', 'sometimes',
@ -413,7 +462,12 @@ class GoodsSkusController extends Controller
'sometimes', 'sometimes',
'required', 'required',
'integer', 'integer',
Rule::in([0, 1, 2])], Rule::in([0, 1, 2])
],
'goal_rate' => [
'sometimes',
'numeric',
],
]; ];
$validator = Validator::make($request->all(), $rules); $validator = Validator::make($request->all(), $rules);
if ($validator->fails()) { if ($validator->fails()) {
@ -451,6 +505,16 @@ class GoodsSkusController extends Controller
if (in_array($updateField, ['reserve', 'loss_num'])) { if (in_array($updateField, ['reserve', 'loss_num'])) {
event(new StockUpdateEvent($sku)); event(new StockUpdateEvent($sku));
} }
// 更新目标去化率
if ('goal_rate' === $updateField) {
DailySalesReport::query()
->where('date', date('Y-m-d'))
->where('goods_sku_id', $sku->id)
->update([
'goal_rate' => $request->$updateField
]);
}
$this->addLog($id, $updateField); $this->addLog($id, $updateField);
end: end:
@ -482,6 +546,11 @@ class GoodsSkusController extends Controller
{ {
$type = $request->get('exportType'); $type = $request->get('exportType');
ob_end_clean(); ob_end_clean();
if ('week_data' === $type) {
$startDate = Carbon::now()->subWeek()->startOfWeek()->toDateString();
$endDate = Carbon::now()->subWeek()->endOfWeek()->toDateString();
return Excel::download(new WeekDataExport($startDate, $endDate), $startDate . '~' . $endDate . '.xlsx');
}
return Excel::download(new GoodsSkusExport($type), $type . '.xlsx'); return Excel::download(new GoodsSkusExport($type), $type . '.xlsx');
} }
@ -541,6 +610,11 @@ class GoodsSkusController extends Controller
$roseNum += $stock; $roseNum += $stock;
continue; continue;
} }
// 多头玫瑰
if (false !== strpos($externalSkuId, 'D')) {
$roseNum += $stock;
continue;
}
// 草花剔除A开头 // 草花剔除A开头
if (false !== strpos($externalSkuId, 'A')) { if (false !== strpos($externalSkuId, 'A')) {
continue; continue;
@ -549,10 +623,14 @@ class GoodsSkusController extends Controller
if (false !== strpos($externalSkuId, 'Z')) { if (false !== strpos($externalSkuId, 'Z')) {
continue; continue;
} }
// 剔除N开头年宵花 // 剔除测试CS
if (false !== strpos($externalSkuId, 'N')) { if (false !== strpos($externalSkuId, 'CS')) {
continue; continue;
} }
// 剔除N开头年宵花
// if (false !== strpos($externalSkuId, 'N')) {
// continue;
// }
$otherNum += $stock; $otherNum += $stock;
} }
@ -561,4 +639,24 @@ class GoodsSkusController extends Controller
'other_num' => $otherNum, 'other_num' => $otherNum,
]); ]);
} }
public function goodsSkusList(Request $request)
{
$title = $request->input('title');
$data = [];
$goodsSkus = GoodsSku::query()
->with('goods:id,title')
->whereHas('goods', function ($query) use ($title) {
$query->where('title', 'like', '%' . $title . '%');
})
->get(['title', 'external_sku_id', 'goods_id']);
foreach ($goodsSkus as $goodsSku) {
$data[] = [
'external_sku_id' => $goodsSku['external_sku_id'],
'title' => $goodsSku['goods']['title'] . ' ' . $goodsSku['title']
];
}
return $data;
}
} }

View File

@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Shop;
use App\Http\Controllers\Controller;
use App\Http\Resources\ShopsResource;
use App\Models\Shop;
use App\Models\ShopSender;
use App\Models\ShopShip;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ShipController extends Controller
{
public function index(Request $request)
{
$shops = Shop::query()
->select(['id', 'name', 'plat_id'])
->with('ship')
// ->where('plat_id', 1)
->get();
$time = date('Y-m-d H:i:s');
foreach ($shops as $shop) {
$faceSheet = new FaceSheet();
$shop->authUrl = $faceSheet->getAuthUrl($shop->id, $shop->plat_id);
$shop->status = 0;
if ($shop->ship) {
$shop->status = $shop->ship->getOriginal('status');
if ($shop->ship->expires_at && $time >= $shop->ship->expires_at) {
ShopShip::query()->where('shop_id', $shop->id)->update(['status' => Shop::$STATUS_UNAUTHORIZED]);
}
}
}
return ShopsResource::collection($shops);
}
public function getSenders(Request $request)
{
$senders = ShopSender::query()
->where('shop_id', $request->get('shop_id'))
->where('shop_ship_id', $request->get('shop_ship_id'))
->orderBy('sort')
->get();
return JsonResource::collection($senders);
}
public function saveSenders(Request $request)
{
$senderList = $request->input('senderList');
foreach ($senderList as $item) {
$item = json_decode($item, true);
$sender = ShopSender::query()->findOrFail($item['id']);
$sender->name = $item['name'];
$sender->mobile = $item['mobile'];
$sender->sort = $item['sort'];
$sender->status = $item['status'];
$sender->timed_delivery_code = $item['timed_delivery_code'];
$sender->save();
}
return response(['message' => '保存成功']);
}
}

View File

@ -4,10 +4,12 @@ namespace App\Http\Controllers\Shop;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\BusinessGoodsSku; use App\Models\BusinessGoodsSku;
use App\Models\DeveloperConfig;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Models\Shop; use App\Models\Shop;
use App\Http\Resources\ShopsResource; use App\Http\Resources\ShopsResource;
use App\Services\PrintModule\Pdd\Ktt; use App\Models\ShopSender;
use App\Services\Business\KuaiTuanTuan\FaceSheet;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
@ -23,6 +25,9 @@ class ShopsController extends Controller
$time = time(); $time = time();
foreach ($shops as $shop) { foreach ($shops as $shop) {
$shop->authUrl = ''; $shop->authUrl = '';
if ('已授权' === $shop->status && ($shop->expires_at - time()) / 3600 <= 72) {
$shop->status = '重新授权';
}
if ('妙选' !== $shop->plat_id && ('未授权' === $shop->status || '重新授权' === $shop->status)) { if ('妙选' !== $shop->plat_id && ('未授权' === $shop->status || '重新授权' === $shop->status)) {
$shop->authUrl = BusinessFactory::init()->make($shop->plat_id)->getAuthUrl($shop->id, $shop->getOriginal('plat_id')); $shop->authUrl = BusinessFactory::init()->make($shop->plat_id)->getAuthUrl($shop->id, $shop->getOriginal('plat_id'));
} }
@ -222,7 +227,36 @@ class ShopsController extends Controller
public function pddPrintAuth(Request $request) public function pddPrintAuth(Request $request)
{ {
$ktt = new Ktt(); [$shopId, $type] = explode('_', $request->get('state'));
$ktt->auth($request->get('code')); $faceSheet = new FaceSheet();
$faceSheet->setCode($request->get('code'));
$faceSheet->setShopWithId($shopId);
$shopShip = $faceSheet->auth('ship', $type);
$resp = $faceSheet->searchWayBill();
if (!isset($resp['pdd_waybill_search_response']['waybill_apply_subscription_cols'])) {
exit();
}
foreach ($resp['pdd_waybill_search_response']['waybill_apply_subscription_cols'] as $subCols) {
foreach ($subCols['branch_account_cols'] as $accountCols) {
foreach ($accountCols['shipp_address_cols'] as $item) {
$item['wp_code'] = $subCols['wp_code'];
ShopSender::query()->updateOrCreate(
['shop_id' => $shopId, 'shop_ship_id' => $shopShip->id, 'detail' => $item['detail']],
$item
);
}
}
}
}
public function orderReset(Request $request)
{
DeveloperConfig::query()->updateOrCreate([
'key' => DeveloperConfig::$ORDER_RESET_TIME,
], [
'value' => date('Y-m-d H:i:s')
]);
return response($this->res, $this->res['httpCode']);
} }
} }

View File

@ -41,6 +41,10 @@ class GoodsSkuRequest extends FormRequest
'sometimes', 'sometimes',
'integer', 'integer',
], ],
'goal_rate' => [
'sometimes',
'numeric',
],
'loss_num' => [ 'loss_num' => [
'sometimes', 'sometimes',
'integer', 'integer',

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class DailySalesReportResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}

View File

@ -2,10 +2,8 @@
namespace App\Imports; namespace App\Imports;
use App\Events\StockUpdateEvent;
use App\Models\CombinationGood; use App\Models\CombinationGood;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Models\GoodsSkuLocation;
use App\Utils\ArrayUtils; use App\Utils\ArrayUtils;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows; use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
@ -72,7 +70,6 @@ class CombinationGoodsImport implements ToArray, SkipsEmptyRows, WithStartRow
CombinationGood::query()->create(['goods_sku_id' => $sku->id, 'item_id' => $skus[$item['item_code']]['id'], 'item_num' => $item['item_num']]); CombinationGood::query()->create(['goods_sku_id' => $sku->id, 'item_id' => $skus[$item['item_code']]['id'], 'item_num' => $item['item_num']]);
} }
DB::commit(); DB::commit();
// event(new StockUpdateEvent($sku, 0, true));
} catch (\Exception $exception) { } catch (\Exception $exception) {
DB::rollBack(); DB::rollBack();
} }

View File

@ -45,29 +45,29 @@ class GoodsSkusImport implements ToCollection, SkipsEmptyRows
} }
unset($row); unset($row);
$validator = Validator::make($collection, [ $validator = Validator::make($collection, [
'*.0' => ['required', 'string', 'max:191'], '*.0' => ['required', 'string', 'max:191'], // 商品名称
'*.1' => ['required', 'string', 'max:191', 'exists:goods_types,name'], '*.1' => ['required', 'string', 'max:191', 'exists:goods_types,name'], // 分类
'*.2' => ['string', 'max:191', 'exists:goods_brands,name'], '*.2' => ['string', 'max:191', 'exists:goods_brands,name'], // 品牌
'*.3' => ['required', 'alpha_dash', 'max:32'], '*.3' => ['required', 'alpha_dash', 'max:32'], // 商品编码
'*.4' => ['required', 'string', 'max:191'], '*.4' => ['required', 'string', 'max:191'], // 规格名称
'*.5' => ['required', 'alpha_dash', 'max:32'], '*.5' => ['required', 'alpha_dash', 'max:32'], // 规格编码
'*.6' => ['required', 'string', Rule::in(['下架', '在售', '预警'])], '*.6' => ['required', 'string', Rule::in(['下架', '在售', '预警'])],
'*.7' => ['required', 'max:10'], '*.7' => ['required', 'max:10'], // 数量
'*.8' => ['required', 'max:10'], '*.8' => ['required', 'max:10'], // 成本
]); ]);
if ($validator->fails()) { if ($validator->fails()) {
throw new ValidationException($validator); throw new ValidationException($validator);
} }
$types = GoodsType::query()->whereIn('name', $types)->get(['id', 'name'])->toArray(); $types = GoodsType::query()->whereIn('name', $types)->pluck('id', 'name')->toArray();
$types = ArrayUtils::index($types, 'name');
$brands = GoodsBrand::query()->whereIn('name', $brands)->get(['id', 'name'])->toArray(); $brands = GoodsBrand::query()->whereIn('name', $brands)->pluck('id', 'name')->toArray();
$brands = ArrayUtils::index($brands, 'name');
$hasGoods = Goods::query()->whereIn('goods_code', $goodsCodes)->get(['id', 'goods_code'])->toArray(); $hasGoods = Goods::query()->whereIn('goods_code', $goodsCodes)->pluck('id', 'goods_code')->toArray();
$hasGoods = ArrayUtils::index($hasGoods, 'goods_code');
$newGoods = $skus = []; $newGoods = $skus = [];
foreach ($collection as $row) { foreach ($collection as $row) {
$sku = [ $sku = [
'goods_id' => $row[3], 'goods_code' => $row[3],
'title' => $row[4], 'title' => $row[4],
'sku_code' => $row[5], 'sku_code' => $row[5],
'status' => $this->statusMap[$row[6]], 'status' => $this->statusMap[$row[6]],
@ -75,15 +75,15 @@ class GoodsSkusImport implements ToCollection, SkipsEmptyRows
'cost' => $row[8], 'cost' => $row[8],
]; ];
// 主商品已存在 // 主商品已存在
if (isset($hasGoods[$row[3]])) { if (isset($hasGoods[$sku['goods_code']])) {
$sku['goods_id'] = $hasGoods[$row[3]]['id']; $sku['goods_id'] = $hasGoods[$sku['goods_code']];
} else { } else {
// 新商品 // 新商品
$newGoods[$row[3]] = [ $newGoods[$sku['goods_code']] = [
'title' => $row[0], 'title' => $row[0],
'type_id' => $types[$row[1]]['id'], 'type_id' => $types[$row[1]],
'brand_id' => $brands[$row[2]]['id'], 'brand_id' => $brands[$row[2]],
'goods_code' => $row[3], 'goods_code' => $sku['goods_code'],
]; ];
} }
$skus[] = $sku; $skus[] = $sku;
@ -93,11 +93,16 @@ class GoodsSkusImport implements ToCollection, SkipsEmptyRows
if ($newGoods) { if ($newGoods) {
$goods = new Goods(); $goods = new Goods();
$goods->batchInsert(array_values($newGoods)); $goods->batchInsert(array_values($newGoods));
$hasGoods = Goods::query()->whereIn('goods_code', array_column($newGoods, 'goods_code'))->get(['id', 'goods_code'])->toArray(); $hasGoods = Goods::query()
->whereIn('goods_code', array_column($newGoods, 'goods_code'))
->get()
->toArray();
$hasGoods = ArrayUtils::index($hasGoods, 'goods_code'); $hasGoods = ArrayUtils::index($hasGoods, 'goods_code');
foreach ($skus as &$newGoodsSku) { foreach ($skus as &$newGoodsSku) {
$newGoodsSku['goods_id'] = isset($hasGoods[$newGoodsSku['goods_id']]) ? $hasGoods[$newGoodsSku['goods_id']]['id'] : $newGoodsSku['goods_id']; $newGoodsSku['goods_id'] = $hasGoods[$newGoodsSku['goods_code']]['id'];
$newGoodsSku['external_sku_id'] = isset($hasGoods[$newGoodsSku['goods_id']]) ? $hasGoods[$newGoodsSku['goods_id']]['goods_code'] . '_' . $newGoodsSku['sku_code'] : ''; $newGoodsSku['external_sku_id'] = $newGoodsSku['goods_code'] . '_' . $newGoodsSku['sku_code'];
$newGoodsSku['name'] = $hasGoods[$newGoodsSku['goods_code']]['title'] . '_' . $newGoodsSku['title'];
unset($newGoodsSku['goods_code']);
} }
unset($newGoodsSku); unset($newGoodsSku);
} }

View File

@ -2,6 +2,8 @@
namespace App\Imports; namespace App\Imports;
use App\Events\BatchStockUpdateEvent;
use App\Jobs\SyncCostToMiaoXuan;
use App\Models\DailyStockRecord; use App\Models\DailyStockRecord;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Models\TodayPrice; use App\Models\TodayPrice;
@ -10,7 +12,6 @@ use Exception;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows; use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Concerns\ToArray; use Maatwebsite\Excel\Concerns\ToArray;
use App\Utils\ArrayUtils; use App\Utils\ArrayUtils;
use App\Events\StockUpdateEvent;
class InventoryImport implements ToArray, SkipsEmptyRows class InventoryImport implements ToArray, SkipsEmptyRows
{ {
@ -54,6 +55,7 @@ class InventoryImport implements ToArray, SkipsEmptyRows
'cost' => $row[4], 'cost' => $row[4],
]); ]);
} }
SyncCostToMiaoXuan::dispatch($row[0], $row[4]);
$updateIds[] = $goodsSku['id']; $updateIds[] = $goodsSku['id'];
DailyStockRecord::query()->where('sku_id', $goodsSku['id'])->where('day', $day)->update([ DailyStockRecord::query()->where('sku_id', $goodsSku['id'])->where('day', $day)->update([
'arrived_today_num' => $row[3], 'arrived_today_num' => $row[3],
@ -96,6 +98,7 @@ class InventoryImport implements ToArray, SkipsEmptyRows
} }
} }
sleep(2); sleep(2);
event(new StockUpdateEvent($onSkuIds, 1)); // 批量更新
event(new BatchStockUpdateEvent($onSkuIds));
} }
} }

View File

@ -2,15 +2,15 @@
namespace App\Imports; namespace App\Imports;
use App\Events\BatchStockUpdateEvent;
use App\Jobs\SyncCostToMiaoXuan;
use App\Models\DailyStockRecord; use App\Models\DailyStockRecord;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Models\TodayPrice;
use App\Utils\DateTimeUtils; use App\Utils\DateTimeUtils;
use Exception; use Exception;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows; use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Concerns\ToArray; use Maatwebsite\Excel\Concerns\ToArray;
use App\Utils\ArrayUtils; use App\Utils\ArrayUtils;
use App\Events\StockUpdateEvent;
class NewSetImport implements ToArray, SkipsEmptyRows class NewSetImport implements ToArray, SkipsEmptyRows
{ {
@ -50,13 +50,17 @@ class NewSetImport implements ToArray, SkipsEmptyRows
'stock' => $row[2] + $goodsSku['stock'], 'stock' => $row[2] + $goodsSku['stock'],
]); ]);
} }
SyncCostToMiaoXuan::dispatch($row[0], $row[3]);
$updateIds[] = $goodsSku['id']; $updateIds[] = $goodsSku['id'];
// 今日到货 // 今日到货
$record = DailyStockRecord::query()->where('sku_id', $goodsSku['id'])->where('day', $day)->first(['id', 'arrived_today_num']); $record = DailyStockRecord::query()
->where('sku_id', $goodsSku['id'])
->where('day', $day)->first(['id', 'arrived_today_num']);
$record->arrived_today_num += $row[2]; $record->arrived_today_num += $row[2];
$record->save(); $record->save();
} }
sleep(2); sleep(2);
event(new StockUpdateEvent($updateIds, 1)); // 批量更新
event(new BatchStockUpdateEvent($updateIds));
} }
} }

View File

@ -14,7 +14,7 @@ class BusinessGoodsSkuIncrQuantity implements ShouldQueue
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $shop; public $shop;
public $businessGoods; public $businessGoodsSku;
public $num; public $num;
public $isIncremental = true; public $isIncremental = true;
@ -23,10 +23,10 @@ class BusinessGoodsSkuIncrQuantity implements ShouldQueue
* *
* @return void * @return void
*/ */
public function __construct($shop, $businessGoods, $num, $isIncremental) public function __construct($shop, $businessGoodsSku, $num, $isIncremental)
{ {
$this->shop = $shop; $this->shop = $shop;
$this->businessGoods = $businessGoods; $this->businessGoodsSku = $businessGoodsSku;
$this->num = $num; $this->num = $num;
$this->isIncremental = $isIncremental; $this->isIncremental = $isIncremental;
} }
@ -38,8 +38,8 @@ class BusinessGoodsSkuIncrQuantity implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
if ($this->businessGoods) { if ($this->businessGoodsSku) {
BusinessFactory::init()->make($this->shop['plat_id'])->setShopWithId($this->shop['id'])->incrQuantity($this->businessGoods, $this->num, $this->isIncremental); BusinessFactory::init()->make($this->shop['plat_id'])->setShopWithId($this->shop['id'])->incrQuantity($this->businessGoodsSku, $this->num, $this->isIncremental);
} }
} }
} }

View File

@ -0,0 +1,50 @@
<?php
namespace App\Jobs;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SyncCostToMiaoXuan implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $externalSkuId;
public $cost;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($externalSkuId, $cost)
{
$this->externalSkuId = $externalSkuId;
$this->cost = $cost;
}
/**
* Execute the job.
*
* @return void
* @throws GuzzleException
*/
public function handle()
{
$url = 'http://shop.chutang66.com/miaoxuan/cost';
$method = 'PUT';
$headers = [
'headers' => ['Content-type' => 'application/x-www-form-urlencoded;charset=UTF-8'],
'form_params' => [
'external_sku_id' => $this->externalSkuId,
'cost' => $this->cost,
]
];
(new Client())->request($method, $url, $headers);
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Listeners;
use App\Events\BatchStockUpdateEvent;
use App\Models\BusinessGoodsSku;
use App\Models\Shop;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use App\Services\Business\BusinessFactory;
class BatchStockUpdateListener implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param BatchStockUpdateEvent $event
* @return void
*/
public function handle(BatchStockUpdateEvent $event)
{
$shops = Shop::query()->whereNotIn('status', [Shop::$STATUS_UNAUTHORIZED, Shop::$STATUS_STOP])->get(['id', 'plat_id']);
if (empty($shops)) {
return;
}
foreach ($shops as $shop) {
foreach ($event->goodsSkus as $goodsSku) {
$num = $goodsSku->stock;
$businessGoodsSkus = BusinessGoodsSku::query()
->select(['goods_id', 'sku_id', 'external_sku_id'])
->where('shop_id', $shop->id)
->where('is_sync', 1)
->where('external_sku_id', $goodsSku->external_sku_id)
->get();
BusinessFactory::init()->make($shop['plat_id'])->setShopWithId($shop['id'])->batchIncrQuantity($businessGoodsSkus->toArray(), $num, false);
usleep(140);
}
}
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Listeners;
use App\Events\CreateLogisticEvent;
use App\Models\Shop;
use App\Models\Waybill;
use App\Services\Business\BusinessFactory;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class CancelLogisticListener implements ShouldQueue
{
public $connection = 'redis';
public $queue = 'listeners';
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param CreateLogisticEvent $event
* @return void
*/
public function handle(CreateLogisticEvent $event)
{
$waybillNo = Waybill::query()
->where('shop_id', $event->shopId)
->where('order_sn', $event->orderSn)
->value('waybill_code');
if (empty($waybillNo)) {
return;
}
$shop = Shop::query()->findOrFail($event->shopId);
$client = BusinessFactory::init()->make($shop['plat_id'])->setShop($shop);
$client->cancelLogistic($event->orderSn, $event->waybillNo);
}
}

View File

@ -3,13 +3,17 @@
namespace App\Listeners; namespace App\Listeners;
use App\Models\CombinationGood; use App\Models\CombinationGood;
use App\Models\DailyStockRecord;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Utils\DateTimeUtils;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use App\Events\StockUpdateEvent; use App\Events\BatchStockUpdateEvent;
class CombinationGoodsStockUpdateListener class CombinationGoodsStockUpdateListener implements ShouldQueue
{ {
use InteractsWithQueue;
/** /**
* Create the event listener. * Create the event listener.
* *
@ -28,18 +32,19 @@ class CombinationGoodsStockUpdateListener
*/ */
public function handle($event) public function handle($event)
{ {
if ($event->combinationGoodsUpdate) { if (!$event->combinationGoodsUpdate) {
return false; return false;
} }
$updateIds = $combinationGoodsIds = $combinationGoodsItemIds = []; $updateIds = $combinationGoodsIds = $combinationGoodsItemIds = [];
if ($event->goodsSku) { if (!empty($event->goodsSku)) {
if ($event->goodsSku->is_combination) { if ($event->goodsSku->is_combination) {
$combinationGoodsIds[] = $event->goodsSku->id; $combinationGoodsIds[] = $event->goodsSku->id;
} else { } else {
$combinationGoodsItemIds[] = $event->goodsSku->id; $combinationGoodsItemIds[] = $event->goodsSku->id;
} }
} }
if ($event->goodsSkus) {
if (!empty($event->goodsSkus)) {
foreach ($event->goodsSkus as $sku) { foreach ($event->goodsSkus as $sku) {
if ($sku->is_combination) { if ($sku->is_combination) {
$combinationGoodsIds[] = $sku->id; $combinationGoodsIds[] = $sku->id;
@ -55,21 +60,20 @@ class CombinationGoodsStockUpdateListener
->whereIn('goods_sku_id', $combinationGoodsIds) ->whereIn('goods_sku_id', $combinationGoodsIds)
->get(); ->get();
foreach ($combinationGoods as $item) { foreach ($combinationGoods as $item) {
$sku = GoodsSku::query()->find($item['item_id']); $goodsSku = GoodsSku::query()->find($item['item_id']);
$sku->stock -= $item['item_num']; $stock = $goodsSku->stock - $item['item_num'];
$sku->save(); [$status, $stock] = $this->checkStatusAndStock($goodsSku, $stock);
$updateIds[] = $sku->id; $goodsSku->status = $status;
$goodsSku->stock = $stock;
$goodsSku->save();
$updateIds[] = $goodsSku->id;
} }
} }
// 计算主商品库存 // 计算主商品库存
if ($combinationGoodsItemIds) { if ($combinationGoodsItemIds) {
$goodsSkuIds = CombinationGood::query() $goodsSkuIds = CombinationGood::query()
->whereIn('item_id', $combinationGoodsItemIds) ->whereIn('item_id', $combinationGoodsItemIds)
->pluck('goods_sku_id') ->pluck('goods_sku_id');
->toArray();
if (empty($goodsSkuIds)) {
return false;
}
foreach ($goodsSkuIds as $goodsSkuId) { foreach ($goodsSkuIds as $goodsSkuId) {
$combinationGoods = CombinationGood::query() $combinationGoods = CombinationGood::query()
->with('goodsSkuItem:id,stock') ->with('goodsSkuItem:id,stock')
@ -80,13 +84,37 @@ class CombinationGoodsStockUpdateListener
$stock[] = (int)($goods['goodsSkuItem']['stock'] / $goods['item_num']); $stock[] = (int)($goods['goodsSkuItem']['stock'] / $goods['item_num']);
} }
$stock = min($stock); $stock = min($stock);
GoodsSku::query()->where('id', $goodsSkuId)->update(['stock' => $stock]); $goodsSku = GoodsSku::query()->find($goodsSkuId);
[$status, $stock] = $this->checkStatusAndStock($goodsSku, $stock);
$goodsSku->status = $status;
$goodsSku->stock = $stock;
$goodsSku->save();
$updateIds[] = $goodsSkuId; $updateIds[] = $goodsSkuId;
} }
} }
if ($updateIds) { if ($updateIds) {
$updateIds = array_unique($updateIds); $updateIds = array_unique($updateIds);
event(new StockUpdateEvent($updateIds, 1, true)); // 批量更新
event(new BatchStockUpdateEvent($updateIds, false));
} }
} }
private function checkStatusAndStock($goodsSku, $stock)
{
if (0 >= $stock) {
$status = GoodsSku::$STATUS_DOWN;
} else {
$status = GoodsSku::$STATUS_ON_SALE;
}
$arrivedTodayNum = DailyStockRecord::query()
->where('day', DateTimeUtils::getToday())
->where('sku_id', $goodsSku->id)
->value('arrived_today_num');
if (20 < $arrivedTodayNum + $goodsSku->yesterday_num && 4 > $stock) {
$status = GoodsSku::$STATUS_DOWN;
$stock = 0;
}
return [$status, $stock];
}
} }

View File

@ -0,0 +1,39 @@
<?php
namespace App\Listeners;
use App\Events\CreateLogisticEvent;
use App\Models\Shop;
use App\Services\Business\BusinessFactory;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class CreateLogisticListener implements ShouldQueue
{
public $connection = 'redis';
public $queue = 'listeners';
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param CreateLogisticEvent $event
* @return void
*/
public function handle(CreateLogisticEvent $event)
{
$shop = Shop::query()->findOrFail($event->shopId);
$client = BusinessFactory::init()->make($shop['plat_id'])->setShop($shop);
$client->createLogistic($event->orderSn, $event->waybillNo);
}
}

View File

@ -3,15 +3,16 @@
namespace App\Listeners; namespace App\Listeners;
use App\Events\StockUpdateEvent; use App\Events\StockUpdateEvent;
use App\Jobs\BusinessGoodsSkuIncrQuantity;
use App\Models\BusinessGoodsSku; use App\Models\BusinessGoodsSku;
use App\Models\Shop; use App\Models\Shop;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use App\Services\Business\BusinessFactory; use App\Services\Business\BusinessFactory;
class StockUpdateListener class StockUpdateListener implements ShouldQueue
{ {
use InteractsWithQueue;
/** /**
* Create the event listener. * Create the event listener.
* *
@ -35,7 +36,6 @@ class StockUpdateListener
return; return;
} }
foreach ($shops as $shop) { foreach ($shops as $shop) {
if (isset($event->goodsSku)) {
$num = $event->goodsSku->stock; $num = $event->goodsSku->stock;
$businessGoodsSkus = BusinessGoodsSku::query() $businessGoodsSkus = BusinessGoodsSku::query()
->select(['goods_id', 'sku_id', 'external_sku_id']) ->select(['goods_id', 'sku_id', 'external_sku_id'])
@ -43,32 +43,7 @@ class StockUpdateListener
->where('is_sync', 1) ->where('is_sync', 1)
->where('external_sku_id', $event->goodsSku->external_sku_id) ->where('external_sku_id', $event->goodsSku->external_sku_id)
->get(); ->get();
if ($event->isBatch) {
BusinessFactory::init()->make($shop['plat_id'])->setShopWithId($shop['id'])->batchIncrQuantity($businessGoodsSkus->toArray(), $num, false); BusinessFactory::init()->make($shop['plat_id'])->setShopWithId($shop['id'])->batchIncrQuantity($businessGoodsSkus->toArray(), $num, false);
} else {
foreach ($businessGoodsSkus as $businessGoodsSku) {
BusinessGoodsSkuIncrQuantity::dispatch($shop, $businessGoodsSku->toArray(), $num, false);
}
}
}
if (isset($event->goodsSkus)) {
foreach ($event->goodsSkus as $goodsSku) {
$num = $goodsSku->stock;
$businessGoodsSkus = BusinessGoodsSku::query()
->select(['goods_id', 'sku_id', 'external_sku_id'])
->where('shop_id', $shop->id)
->where('is_sync', 1)
->where('external_sku_id', $goodsSku->external_sku_id)
->get();
if ($event->isBatch) {
BusinessFactory::init()->make($shop['plat_id'])->setShopWithId($shop['id'])->batchIncrQuantity($businessGoodsSkus->toArray(), $num, false);
} else {
foreach ($businessGoodsSkus as $businessGoodsSku) {
BusinessGoodsSkuIncrQuantity::dispatch($shop, $businessGoodsSku->toArray(), $num, false);
}
}
}
}
} }
} }
} }

View File

@ -1,57 +0,0 @@
<?php
namespace App\Listeners;
use App\Models\GoodsSku;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class StockWarning implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
public function handle($event)
{
if (isset($event->goodsSku)) {
$event->goodsSku->status = 2;
if (0 >= $event->goodsSku->stock) {
$event->goodsSku->status = 0;
}
if (5 < $event->goodsSku->stock) {
$event->goodsSku->status = 1;
}
$event->goodsSku->save();
}
if (isset($event->goodsSkus)) {
$warningIds = $normalIds = $downIds = [];
foreach ($event->goodsSkus as $goodsSku) {
if (0 >= $goodsSku['stock']) {
$downIds[] = $goodsSku['id'];
} elseif (5 < $goodsSku['stock']) {
$normalIds[] = $goodsSku['id'];
} else {
$warningIds[] = $goodsSku['id'];
}
}
if ($warningIds) {
GoodsSku::query()->whereIn('id', $warningIds)->update(['status' => 2]);
}
if ($normalIds) {
GoodsSku::query()->whereIn('id', $normalIds)->update(['status' => 1]);
}
if ($downIds) {
GoodsSku::query()->whereIn('id', $downIds)->update(['status' => 0]);
}
}
}
}

View File

@ -29,7 +29,7 @@ class UpdateBusinessGoodsStock implements ShouldQueue
* Handle the event. * Handle the event.
* *
* @param BusinessOrdersUpdate $event * @param BusinessOrdersUpdate $event
* @return void * @return false|void
*/ */
public function handle(BusinessOrdersUpdate $event) public function handle(BusinessOrdersUpdate $event)
{ {
@ -41,17 +41,19 @@ class UpdateBusinessGoodsStock implements ShouldQueue
$log->target_id = $event->goodsSku->id ?? 0; $log->target_id = $event->goodsSku->id ?? 0;
$log->target_field = 'stock'; $log->target_field = 'stock';
$log->user_id = 999; $log->user_id = 999;
$log->message = '未找到' . json_encode($event->businessGoods, 256); $log->message = '未找到' . json_encode($event->businessGoodSku, 256);
$log->save(); $log->save();
return false; return false;
} }
$builder = Shop::query()->whereNotIn('status', [Shop::$STATUS_UNAUTHORIZED, Shop::$STATUS_STOP]); $builder = Shop::query()->whereNotIn('status', [Shop::$STATUS_UNAUTHORIZED, Shop::$STATUS_STOP]);
// 非订单影响库存变更,只更新本店铺下商品 // 非订单影响库存变更,只更新本店铺下商品
if (!isset($event->businessGoods['business_order_id'])) { if (!isset($event->businessGoodSku['business_order_id'])) {
$builder->where('id', $event->businessGoods['shop_id']); $builder->where('id', $event->businessGoodSku['shop_id']);
} }
$shops = $builder->get(['id', 'plat_id']); $shops = $builder->get(['id', 'plat_id']);
if (empty($shops)) { if (empty($shops)) {
LogFile::info('可操作店铺为空'); LogFile::info('可操作店铺为空');
return false; return false;
@ -59,9 +61,14 @@ class UpdateBusinessGoodsStock implements ShouldQueue
foreach ($shops as $shop) { foreach ($shops as $shop) {
$num = $event->goodsSku->stock; $num = $event->goodsSku->stock;
$businessGoodsSkus = BusinessGoodsSku::query()->where('shop_id', $shop->id)->where('is_sync', 1)->where('external_sku_id', $event->businessGoods['external_sku_id'])->get(); $businessGoodsSkus = BusinessGoodsSku::query()
->where('shop_id', $shop->id)
->where('is_sync', 1)
->where('external_sku_id', $event->businessGoodSku['external_sku_id'])
->get();
foreach ($businessGoodsSkus as $businessGoodsSku) { foreach ($businessGoodsSkus as $businessGoodsSku) {
BusinessGoodsSkuIncrQuantity::dispatch($shop, $businessGoodsSku->toArray(), $num, false); BusinessGoodsSkuIncrQuantity::dispatch($shop, $businessGoodsSku->toArray(), $num, false);
usleep(140);
} }
} }
} }

View File

@ -17,6 +17,12 @@ class BusinessOrder extends Model
'cancel_status', 'cancel_status',
'after_sales_status', 'after_sales_status',
'supply_participate_no', 'supply_participate_no',
'confirm_at_start',
'confirm_at_end',
'goods_sku_num',
'print_status',
'ids',
'pno',
]; ];
protected $fillable = [ protected $fillable = [
@ -63,21 +69,21 @@ class BusinessOrder extends Model
public function getShippingStatusAttribute($value) public function getShippingStatusAttribute($value)
{ {
$map = ['未发货', '已发货', '部分发货']; $map = ['未发货', '已发货', '部分发货', '' => ''];
return $map[$value]; return $map[$value];
} }
public function getIsSupplierAttribute($value) public function getIsSupplierAttribute($value)
{ {
$map = ['帮忙团订单', '自卖团订单']; $map = ['帮忙团订单', '自卖团订单', '' => ''];
return $map[$value]; return $map[$value];
} }
public function getCancelStatusAttribute($value) public function getCancelStatusAttribute($value)
{ {
$map = ['未取消', '已取消']; $map = ['未取消', '已取消', '' => ''];
return $map[$value]; return $map[$value];
} }

View File

@ -44,4 +44,9 @@ class BusinessOrderItem extends Model
{ {
return $this->hasOne(GoodsSkuLocation::class, 'external_sku_id', 'external_sku_id'); return $this->hasOne(GoodsSkuLocation::class, 'external_sku_id', 'external_sku_id');
} }
public function goodsSku()
{
return $this->hasOne(GoodsSku::class, 'external_sku_id', 'external_sku_id');
}
} }

View File

@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DailyReport extends Model
{
protected $guarded = [];
protected $casts = [
'shop_data' => 'array',
];
public function goods()
{
return $this->hasOne(Goods::class, 'id', 'goods_id');
}
public function goodsBrand()
{
return $this->belongsTo(GoodsBrand::class, 'brand_id', 'id');
}
public function goodsType()
{
return $this->belongsTo(GoodsType::class, 'type_id', 'id');
}
public function goodsSku()
{
return $this->hasOne(GoodsSku::class, 'id', 'goods_sku_id');
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Models;
use App\Models\traits\Filter;
use Illuminate\Database\Eloquent\Model;
class DailySalesReport extends Model
{
use Filter;
protected $guarded = [];
public $fieldSearchable = [
'date',
'name' => 'like',
'sales_num' => '>',
];
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DeveloperConfig extends Model
{
protected $guarded = [];
public static $ORDER_RESET_TIME = 'order_reset_time';
}

View File

@ -10,7 +10,6 @@ class Goods extends Model
//查询字段 //查询字段
public $fieldSearchable = [ public $fieldSearchable = [
'goods_title',
'type_id', 'type_id',
'brand_id', 'brand_id',
]; ];

View File

@ -13,7 +13,8 @@ class GoodsSku extends Model
'sku_title', 'sku_title',
'status', 'status',
'exclude_ids', 'exclude_ids',
'external_sku_id' 'external_sku_id',
'is_combination',
]; ];
protected $fillable = [ protected $fillable = [
@ -31,10 +32,14 @@ class GoodsSku extends Model
'thumb_url', 'thumb_url',
'external_sku_id', 'external_sku_id',
'is_combination', 'is_combination',
'name',
]; ];
protected $hidden = ['created_at']; protected $hidden = ['created_at'];
public static $STATUS_ON_SALE = 1;
public static $STATUS_DOWN = 0;
/** /**
* 获取状态 * 获取状态
* *

View File

@ -26,7 +26,6 @@ class Shop extends Model
'access_token', 'access_token',
'expires_in', 'expires_in',
'refresh_token', 'refresh_token',
'refresh_token_expires_at',
'refresh_token_expires_in', 'refresh_token_expires_in',
'pop_auth_token_create_response', 'pop_auth_token_create_response',
]; ];
@ -42,10 +41,8 @@ class Shop extends Model
1 => '已授权', 1 => '已授权',
2 => '无需授权', 2 => '无需授权',
3 => '停用', 3 => '停用',
'重新授权' => '重新授权',
]; ];
if (1 === (int)$value && ($this->attributes['expires_at'] - time()) / 3600 <= 72) {
return '重新授权';
}
return $map[$value]; return $map[$value];
} }
@ -61,4 +58,14 @@ class Shop extends Model
return $map[$value]; return $map[$value];
} }
public function getRefreshTokenExpiresAtAttribute($value)
{
return $value ? date('Y-m-d H:i:s', $value) : '';
}
public function ship()
{
return $this->hasOne(ShopShip::class, 'shop_id', 'id');
}
} }

10
app/Models/ShopSender.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ShopSender extends Model
{
protected $guarded = [];
}

49
app/Models/ShopShip.php Normal file
View File

@ -0,0 +1,49 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ShopShip extends Model
{
protected $guarded = [];
public function shop()
{
return $this->belongsTo(Shop::class, 'shop_id', 'id');
}
public function getStatusAttribute($value)
{
$map = [
0 => '未授权',
1 => '已授权',
3 => '停用',
];
if (1 === (int)$value && ($this->attributes['expires_at'] - time()) / 3600 <= 72) {
return '重新授权';
}
return $map[$value];
}
public function getExpiresAtAttribute($value)
{
return $value ? date('Y-m-d H:i:s', $value) : '';
}
public function getTypeAttribute($value)
{
$map = [
'normal' => '电商标快',
'air' => '空运',
];
return $map[$value];
}
public function senders()
{
return $this->hasMany(ShopSender::class, 'shop_ship_id');
}
}

13
app/Models/Waybill.php Normal file
View File

@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Waybill extends Model
{
protected $guarded = [];
public static $BUSINESS_EXPRESS_CODE = 247;
public static $AIR_FREIGHT_CODE = 266;
}

View File

@ -3,14 +3,19 @@
namespace App\Providers; namespace App\Providers;
use App\Events\BusinessOrdersUpdate; use App\Events\BusinessOrdersUpdate;
use App\Events\GroupSetEvent;
use App\Events\StockUpdateEvent; use App\Events\StockUpdateEvent;
use App\Events\GroupSetEvent;
use App\Events\BatchStockUpdateEvent;
use App\Listeners\BatchStockUpdateListener;
use App\Listeners\CreateLogisticListener;
use App\Listeners\GroupQueryListener; use App\Listeners\GroupQueryListener;
use App\Listeners\StockUpdateListener; use App\Listeners\StockUpdateListener;
use App\Listeners\StockWarning;
use App\Listeners\CombinationGoodsStockUpdateListener; use App\Listeners\CombinationGoodsStockUpdateListener;
use App\Listeners\UpdateBusinessGoodsStock; use App\Listeners\UpdateBusinessGoodsStock;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use App\Events\BusinessOrderCancelEvent;
use App\Listeners\CancelLogisticListener;
use App\Events\CreateLogisticEvent;
class EventServiceProvider extends ServiceProvider class EventServiceProvider extends ServiceProvider
{ {
@ -22,17 +27,25 @@ class EventServiceProvider extends ServiceProvider
protected $listen = [ protected $listen = [
BusinessOrdersUpdate::class => [ BusinessOrdersUpdate::class => [
UpdateBusinessGoodsStock::class, UpdateBusinessGoodsStock::class,
StockWarning::class, CombinationGoodsStockUpdateListener::class,
],
BatchStockUpdateEvent::class => [
BatchStockUpdateListener::class,
CombinationGoodsStockUpdateListener::class, CombinationGoodsStockUpdateListener::class,
], ],
StockUpdateEvent::class => [ StockUpdateEvent::class => [
StockUpdateListener::class, StockUpdateListener::class,
StockWarning::class,
CombinationGoodsStockUpdateListener::class, CombinationGoodsStockUpdateListener::class,
], ],
GroupSetEvent::class => [ GroupSetEvent::class => [
GroupQueryListener::class, GroupQueryListener::class,
], ],
BusinessOrderCancelEvent::class => [
CancelLogisticListener::class
],
CreateLogisticEvent::class => [
CreateLogisticListener::class
],
]; ];
/** /**

View File

@ -2,6 +2,7 @@
namespace App\Services\Business; namespace App\Services\Business;
use App\Events\BusinessOrderCancelEvent;
use App\Events\BusinessOrdersUpdate; use App\Events\BusinessOrdersUpdate;
use App\Models\BusinessGoodsSku; use App\Models\BusinessGoodsSku;
use App\Models\BusinessOrder; use App\Models\BusinessOrder;
@ -29,6 +30,10 @@ abstract class BusinessClient
abstract public function bindGoods($goods); abstract public function bindGoods($goods);
abstract public function createLogistic($orderSn, $waybillNo);
abstract public function cancelLogistic($orderSn, $waybillNo);
abstract public function incrQuantity($businessGoodsSku, $num, $incremental); abstract public function incrQuantity($businessGoodsSku, $num, $incremental);
abstract public function downloadOrdersAndSave($beginTime, $endTime, $downloadType = 'default', $page = 1); abstract public function downloadOrdersAndSave($beginTime, $endTime, $downloadType = 'default', $page = 1);
@ -45,9 +50,23 @@ abstract class BusinessClient
} else { } else {
$orderRecord->update($order); $orderRecord->update($order);
} }
if (!empty($order['cancel_status'])) {
event(new BusinessOrderCancelEvent($shopId, $order['order_sn']));
}
$goodsSkuNum = 0;
foreach ($order['sub_order_list'] as $item) { foreach ($order['sub_order_list'] as $item) {
$item['shop_id'] = $shopId; $item['shop_id'] = $shopId;
$orderItem = BusinessOrderItem::firstOrNew(['shop_id' => $shopId, 'business_order_id' => $orderRecord->id, 'goods_id' => $item['goods_id'], 'sku_id' => $item['sku_id']], $item); $orderItem = BusinessOrderItem::firstOrNew(['shop_id' => $shopId, 'business_order_id' => $orderRecord->id, 'goods_id' => $item['goods_id'], 'sku_id' => $item['sku_id']], $item);
if ($item['external_sku_id']) {
$goodsSku = GoodsSku::query()
->with('combinationGoods')
->where('external_sku_id', $item['external_sku_id'])
->first('external_sku_id');
$combinationNum = $goodsSku ? ($goodsSku->combinationGoods->count() ?: 1) : 1;
$goodsSkuNum += $combinationNum;
} else {
$goodsSkuNum++;
}
$num = 0; $num = 0;
$cancelNum = $item['already_cancel_number'] ?? 0; $cancelNum = $item['already_cancel_number'] ?? 0;
if (empty($orderItem->id)) { if (empty($orderItem->id)) {
@ -78,13 +97,15 @@ abstract class BusinessClient
event(new BusinessOrdersUpdate($orderItem, $num)); event(new BusinessOrdersUpdate($orderItem, $num));
} }
} }
$orderRecord->goods_sku_num = $goodsSkuNum;
$orderRecord->save();
} }
} }
public function authCallback($code, $shopId) public function authCallback($code, $shop)
{ {
$this->setCode($code); $this->setCode($code);
$this->setShop($shopId); $this->setShop($shop);
$this->auth(); $this->auth();
return $this; return $this;
@ -97,7 +118,7 @@ abstract class BusinessClient
return $this; return $this;
} }
public function setShop(Shop $shop) public function setShop($shop)
{ {
$this->shop = $shop; $this->shop = $shop;
@ -121,18 +142,6 @@ abstract class BusinessClient
return $this->code; return $this->code;
} }
public function setSkuId($skuId)
{
$this->skuId = $skuId;
return $this;
}
public function getSkuId()
{
return $this->skuId;
}
public function formDataPostRequest($url, $params) public function formDataPostRequest($url, $params)
{ {
$method = 'POST'; $method = 'POST';
@ -143,6 +152,10 @@ abstract class BusinessClient
$res = (new Client())->request($method, $url, $headers); $res = (new Client())->request($method, $url, $headers);
$size = $res->getBody()->getSize(); $size = $res->getBody()->getSize();
$res = json_decode($res->getBody()->getContents(), true); $res = json_decode($res->getBody()->getContents(), true);
$paramsJson = json_encode($params, 256);
if (strlen($paramsJson) > 1024) {
$paramsJson = '';
}
if (!in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list'], true)) { if (!in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list'], true)) {
$log = new Log(); $log = new Log();
$log->module = 'plat'; $log->module = 'plat';
@ -152,14 +165,14 @@ abstract class BusinessClient
$log->target_field = $params['type']; $log->target_field = $params['type'];
$log->user_id = Auth::id() ?? 999; $log->user_id = Auth::id() ?? 999;
if ($size < 48000) { if ($size < 48000) {
$log->message = json_encode($res, 256) . '=====' . json_encode($params, 256); $log->message = json_encode($res, 256) . '=====' . $paramsJson;
} else { } else {
$log->message = json_encode($params, 256); $log->message = $paramsJson;
} }
$log->save(); $log->save();
} }
if (in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list'], true)) { if (in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list'], true)) {
LogFile::info('快团团请求: ' . json_encode($params, 256)); LogFile::info('快团团请求: ' . $paramsJson);
LogFile::info('快团团返回: ' . json_encode($res, 256)); LogFile::info('快团团返回: ' . json_encode($res, 256));
} }
@ -177,6 +190,8 @@ abstract class BusinessClient
]; ];
$promises[] = $client->postAsync($url, $options); $promises[] = $client->postAsync($url, $options);
} }
if ($promises) {
Promise\Utils::unwrap($promises); Promise\Utils::unwrap($promises);
} }
}
} }

View File

@ -15,6 +15,10 @@ class BusinessFactory
$this->platList['妙选'] = MiaoXuan::class; $this->platList['妙选'] = MiaoXuan::class;
} }
/**
* @param $platName
* @return BusinessClient
*/
public function make($platName) public function make($platName)
{ {
return new $this->platList[$platName]; return new $this->platList[$platName];

View File

@ -4,6 +4,21 @@ namespace App\Services\Business\KuaiTuanTuan;
class FaceSheet extends KuaiTuanTuan class FaceSheet extends KuaiTuanTuan
{ {
protected $clientId = '24f25877aca447c5830a6aa896301d5e';
protected $clientSecret = '59b6f4bd402c6423878a8f4ef1bde28359c1f05a';
protected $redirectUri = 'http://erp.chutang66.com/pdd/ship';
public function __construct()
{
}
public function getAuthUrl($shopId, $platId)
{
$state = $shopId . '_' . $platId;
return "https://wb.pinduoduo.com/logistics/auth?client_id={$this->clientId}&redirect_uri={$this->redirectUri}&state={$state}";
}
/** /**
* 快递公司查看接口 * 快递公司查看接口
* *
@ -17,6 +32,21 @@ class FaceSheet extends KuaiTuanTuan
return $this->doRequest($type, $appendParams); return $this->doRequest($type, $appendParams);
} }
public function getWayBill($sender, $orderInfo, $wpCode)
{
$type = 'pdd.waybill.get';
$appendParams = [
'param_waybill_cloud_print_apply_new_request' => [
'sender' => $sender,
'trade_order_info_dtos' => [$orderInfo],
'wp_code' => $wpCode,
]
];
$appendParams['param_waybill_cloud_print_apply_new_request'] = json_encode($appendParams['param_waybill_cloud_print_apply_new_request'], 256);
return $this->doRequest($type, $appendParams);
}
/** /**
* searchWayBill * searchWayBill
* *
@ -38,10 +68,33 @@ class FaceSheet extends KuaiTuanTuan
return $this->doRequest($type, $appendParams); return $this->doRequest($type, $appendParams);
} }
public function test() public function getStdTemplates()
{ {
$type = 'pdd.cloudprint.stdtemplates.get'; $type = 'pdd.cloudprint.stdtemplates.get';
$appendParams = []; $appendParams = [
'wp_code' => 'SF'
];
return $this->doRequest($type, $appendParams);
}
public function getCustomares($templateId = 118)
{
$type = 'pdd.cloudprint.customares.get';
$appendParams = [
'template_id' => $templateId
];
return $this->doRequest($type, $appendParams);
}
public function cancel($waybillCode, $wpCode)
{
$type = 'pdd.cloudprint.customares.get';
$appendParams = [
'waybill_code' => $waybillCode,
'wp_code' => $wpCode,
];
return $this->doRequest($type, $appendParams); return $this->doRequest($type, $appendParams);
} }

View File

@ -5,6 +5,8 @@ namespace App\Services\Business\KuaiTuanTuan;
use App\Models\BusinessGoodsSku; use App\Models\BusinessGoodsSku;
use App\Models\GoodsSku; use App\Models\GoodsSku;
use App\Models\GroupGoods; use App\Models\GroupGoods;
use App\Models\Shop;
use App\Models\ShopShip;
use App\Services\Business\BusinessClient; use App\Services\Business\BusinessClient;
use App\Models\Groups as GroupsModel; use App\Models\Groups as GroupsModel;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -29,13 +31,22 @@ class KuaiTuanTuan extends BusinessClient
'sign' => '' 'sign' => ''
]; ];
public function auth() public function auth($type = 'ktt', $shipType = 'normal')
{ {
$accessToken = $this->getAccessTokenWithCode(); $accessToken = $this->getAccessTokenWithCode();
$accessToken['scope'] = json_encode($accessToken['scope'], 256); $accessToken['scope'] = json_encode($accessToken['scope'], 256);
$accessToken['status'] = Shop::$STATUS_AUTHORIZED;
if ('ktt' === $type) {
$accessToken['pop_auth_token_create_response'] = json_encode($accessToken, 256); $accessToken['pop_auth_token_create_response'] = json_encode($accessToken, 256);
$accessToken['status'] = 1;
$this->shop->update($accessToken); $this->shop->update($accessToken);
}
if ('ship' === $type) {
unset($accessToken['r1_expires_at'], $accessToken['r1_expires_in'], $accessToken['r2_expires_at'], $accessToken['r2_expires_in'], $accessToken['w1_expires_at'], $accessToken['w1_expires_in'], $accessToken['w2_expires_at'], $accessToken['w2_expires_in'], $accessToken['request_id']);
$this->shop = ShopShip::query()->updateOrCreate(
['shop_id' => $this->shop->id, 'type' => $shipType],
$accessToken
);
}
return $this->shop; return $this->shop;
} }
@ -66,8 +77,10 @@ class KuaiTuanTuan extends BusinessClient
public function batchIncrQuantity($businessGoodsSkus, $num, $incremental) public function batchIncrQuantity($businessGoodsSkus, $num, $incremental)
{ {
$batchAppendParams = []; $batchAppendParams = [];
$logData = [];
foreach ($businessGoodsSkus as $businessGoodsSku) { foreach ($businessGoodsSkus as $businessGoodsSku) {
[$type, $appendParams] = Goods::incrQuantity($businessGoodsSku['goods_id'], $businessGoodsSku['sku_id'], $num, $incremental ? 1 : 2); [$type, $appendParams] = Goods::incrQuantity($businessGoodsSku['goods_id'], $businessGoodsSku['sku_id'], $num, $incremental ? 1 : 2);
$logData[] = $appendParams;
$appendParams['type'] = $type; $appendParams['type'] = $type;
$appendParams['client_id'] = $this->clientId; $appendParams['client_id'] = $this->clientId;
$appendParams['timestamp'] = time(); $appendParams['timestamp'] = time();
@ -76,6 +89,9 @@ class KuaiTuanTuan extends BusinessClient
$batchAppendParams[] = $appendParams; $batchAppendParams[] = $appendParams;
} }
$this->batchAsyncPostRequest('https://gw-api.pinduoduo.com/api/router', $batchAppendParams); $this->batchAsyncPostRequest('https://gw-api.pinduoduo.com/api/router', $batchAppendParams);
if ($logData) {
Log::info('本次批量同步: ' . json_encode($logData, 256));
}
} }
/** /**
@ -153,7 +169,7 @@ class KuaiTuanTuan extends BusinessClient
if (50001 == $res['error_response']['error_code'] && in_array($res['error_response']['sub_code'], ['13', '11'])) { if (50001 == $res['error_response']['error_code'] && in_array($res['error_response']['sub_code'], ['13', '11'])) {
BusinessGoodsSku::query()->where('goods_id', $appendParams['goods_id'])->where('sku_id', $appendParams['sku_id'])->update(['is_sync' => 0]); BusinessGoodsSku::query()->where('goods_id', $appendParams['goods_id'])->where('sku_id', $appendParams['sku_id'])->update(['is_sync' => 0]);
} }
Log::error(json_encode($res, 256)); Log::error(json_encode($res, 256) . $type);
} }
return $res; return $res;
@ -251,4 +267,25 @@ class KuaiTuanTuan extends BusinessClient
return $this->doRequest($type, $appendParams); return $this->doRequest($type, $appendParams);
} }
public function createLogistic($orderSn, $waybillNo)
{
[$type, $appendParams] = Order::createOrderLogistic($orderSn, $waybillNo);
return $this->doRequest($type, $appendParams);
}
public function cancelLogistic($orderSn, $waybillNo)
{
[$type, $appendParams] = Order::deleteOrderLogistic($orderSn, $waybillNo);
return $this->doRequest($type, $appendParams);
}
public function getLogisticsCompanies()
{
[$type, $appendParams] = Order::getLogisticsCompanies();
return $this->doRequest($type, $appendParams);
}
} }

View File

@ -57,5 +57,40 @@ class Order
return [$type, $appendParams]; return [$type, $appendParams];
} }
public static function getLogisticsCompanies()
{
$type = 'pdd.logistics.companies.get';
return [$type, []];
}
public static function createOrderLogistic($orderSn, $waybillNo, $logisticsId = 44, $logisticsName = '顺丰快递')
{
// 不支持拆单发货
$type = 'pdd.ktt.order.logistic.create';
$appendParams = [
'logisticsId' => $logisticsId,
'logisticsName' => $logisticsName,
'orderSn' => $orderSn,
// 'subOrderSnList' => [], 发货子单号列表,无子单号视为整单发货
'waybillNo' => $waybillNo,
];
return [$type, $appendParams];
}
public static function deleteOrderLogistic($orderSn, $waybillNo)
{
$type = 'pdd.ktt.order.logistic.delete';
$appendParams = [
'orderSn' => $orderSn,
// 'subOrderSnList' => [], 发货子单号列表,无子单号视为整单发货
'waybillNo' => $waybillNo,
];
return [$type, $appendParams];
}
} }

View File

@ -47,4 +47,14 @@ class MiaoXuan extends BusinessClient
{ {
// TODO: Implement downloadGoods() method. // TODO: Implement downloadGoods() method.
} }
public function createLogistic($orderSn, $waybillNo)
{
// TODO: Implement createLogistic() method.
}
public function cancelLogistic($orderSn, $waybillNo)
{
// TODO: Implement cancelLogistic() method.
}
} }

View File

@ -1,63 +0,0 @@
<?php
namespace App\Services\PrintModule\Pdd;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log as LogFile;
class Ktt
{
protected $clientId = '24f25877aca447c5830a6aa896301d5e';
protected $clientSecret = '59b6f4bd402c6423878a8f4ef1bde28359c1f05a';
protected $callBackUrl = 'http://erp.chutang66.com/pdd/ship';
public function auth($code)
{
$accessToken = $this->getAccessTokenWithCode($code);
LogFile::info('电子面单应用授权: ' . json_encode($accessToken, 256));
}
protected function getAccessTokenWithCode($code)
{
$type = 'pdd.pop.auth.token.create';
$res = $this->doRequest($type, ['code' => $code]);
return $res['pop_auth_token_create_response'];
}
public function doRequest($type, $appendParams = [], $url = 'https://gw-api.pinduoduo.com/api/router')
{
$publicParams = [
'type' => $type,
'client_id' => $this->clientId,
'timestamp' => time()
];
$publicParams = array_merge($publicParams, $appendParams);
$publicParams['sign'] = $this->getSign($publicParams);
return $this->formDataPostRequest($url, $publicParams);
}
protected function getSign($params)
{
ksort($params);
$str = '';
foreach ($params as $key => $val) {
$str .= $key . $val;
}
$str = $this->clientSecret . $str . $this->clientSecret;
return strtoupper(md5($str));
}
protected function formDataPostRequest($url, $params)
{
$method = 'POST';
$headers = [
'headers' => ['Content-type' => 'application/x-www-form-urlencoded;charset=UTF-8'],
'form_params' => $params
];
$res = (new Client())->request($method, $url, $headers);
return json_decode($res->getBody()->getContents(), true);
}
}

View File

@ -0,0 +1,356 @@
<?php
namespace App\Services\Ship;
use App\Events\CreateLogisticEvent;
use App\Models\GoodsSku;
use App\Models\ShopShip;
use App\Models\Waybill;
use App\Services\Business\KuaiTuanTuan\FaceSheet;
class WayBillService
{
public $orders;
public $objectId;
// 快递便携式一联单
public $sfOne = 'https://file-link.pinduoduo.com/sf_one';
public $sfOneTemplate = 'sf_one1688973997895.xml';
// 快递三联面单
public $sfThree = 'https://file-link.pinduoduo.com/sf_three';
public $sfThreeTemplate = 'sf_three1677381064344.xml';
// 标准模板
public $sfStd = 'https://file-link.pinduoduo.com/sf_std';
public $sfStdTemplate = 'sf_std1677380804913.xml';
// 时效类型
public $timedDeliveryCode;
public function getContents()
{
// 已下单过的订单不再下单
$contents = [];
foreach ($this->orders as $shopId => $order) {
// 订单取消的情况暂不处理
$shopShip = $this->getShopShip($shopId);
$faceSheet = new FaceSheet();
$faceSheet->setShop($shopShip);
foreach ($order as $item) {
[$sender, $orderInfo, $senderConfig] = $this->prepareRequest($item, $shopShip);
$waybill = $this->saveWayBill($item, $shopShip, $senderConfig);
if (empty($waybill->id)) {
$resp = $faceSheet->getWayBill($sender, $orderInfo, $senderConfig['wp_code']);
if (isset($resp['pdd_waybill_get_response'])) {
$data = $resp['pdd_waybill_get_response']['modules'][0];
$printData = json_decode($data['print_data'], true);
$waybill->request_id = $resp['pdd_waybill_get_response']['request_id'];
$waybill->encryptedData = $printData['encryptedData'];
$waybill->signature = $printData['signature'];
$waybill->templateUrl = $printData['templateUrl'];
$waybill->ver = $printData['ver'];
$waybill->waybill_code = $data['waybill_code'];
$waybill->save();
// 返回待打印内容
$contents[$waybill->id] = [
'addData' => [
'sender' => $sender,
],
'encryptedData' => $printData['encryptedData'],
'signature' => $printData['signature'],
'templateUrl' => env('APP_URL') . '/ktt/' . $this->sfOneTemplate, // 电子面单模板
'ver' => $printData['ver'],
'userid' => $waybill->user_id,
'items' => $item['items'],
'documentID' => $waybill->id,
'order_id' => $item['id'],
'participate_no' => $waybill->participate_no,
'note' => $waybill->note,
];
event(new CreateLogisticEvent($shopShip->shop_id, $waybill->order_sn, $data['waybill_code']));
}
} else {
$contents[$waybill->id] = [
'addData' => [
'sender' => $sender,
],
'encryptedData' => $waybill->encryptedData,
'signature' => $waybill->signature,
'templateUrl' => env('APP_URL') . '/ktt/' . $this->sfOneTemplate, // 电子面单模板
'ver' => $waybill->ver,
'userid' => $waybill->user_id,
'items' => json_decode($waybill->items, true),
'documentID' => $waybill->id,
'order_id' => $item['id'],
'participate_no' => $waybill->participate_no,
'note' => $waybill->note,
];
}
}
}
return $contents;
}
public function getDocumentsAndOrderIds($contents)
{
// 打印单,根据商品排序
$items = [];
foreach ($contents as $docId => $content) {
foreach ($content['items'] as $item) {
if ($item['is_single']) {
$items[$item['external_sku_id']][] = $docId;
}
}
}
ksort($items);
$documents = $orderIds = $hasIds = [];
foreach ($items as $docIds) {
$docIds = array_unique($docIds);
$docIds = array_diff($docIds, $hasIds);
$hasIds = array_merge($hasIds, $docIds);
foreach ($docIds as $docId) {
$orderIds[] = $contents[$docId]['order_id'];
$documents[] = $contents[$docId];
}
}
return [$documents, $orderIds];
}
private function saveWayBill($order, $shopShip, $senderConfig)
{
$waybill = Waybill::query()->firstOrNew(
['order_sn' => $order['order_sn']]
);
$waybill->shop_id = $shopShip->shop_id;
$waybill->object_id = $this->objectId;
$waybill->sender_country = $senderConfig['country'];
$waybill->sender_province = $senderConfig['province'];
$waybill->sender_city = $senderConfig['city'];
$waybill->sender_district = $senderConfig['district'];
$waybill->sender_detail = $senderConfig['detail'];
$waybill->sender_name = $senderConfig['name'];
$waybill->sender_mobile = $senderConfig['mobile'];
$waybill->recipient_province = $order['recipient_province'];
$waybill->recipient_city = $order['recipient_city'];
$waybill->recipient_district = $order['recipient_district'];
$waybill->recipient_detail = $order['recipient_detail'];
$waybill->recipient_name = $order['recipient_name'];
$waybill->recipient_mobile = $order['recipient_mobile'];
$waybill->user_id = $shopShip->owner_id;
$waybill->wp_code = $senderConfig['wp_code'];
$waybill->order_sn = $order['order_sn'];
$waybill->order_id = $order['id'];
$waybill->participate_no = $order['participate_no'];
$waybill->note = $order['note'];
$waybill->items = json_encode($order['items'], 256);
return $waybill;
}
private function getTimedDelivery($order)
{
$this->timedDeliveryCode = Waybill::$BUSINESS_EXPRESS_CODE;
$address = [
'辽宁省' => [],
'吉林省' => [],
'黑龙江省' => [],
'甘肃省' => [],
'海南省' => [],
'宁夏回族自治区' => [
'银川市', '石嘴山市', '吴忠市', '固原市'
],
'青海省' => [
'西宁市', '海东市', '海北藏族自治州', '黄南藏族自治州', '海南藏族自治州', '玉树藏族自治州'
],
];
if (isset($address[$order['recipient_province']])) {
if (empty($address[$order['recipient_province']])) {
$this->timedDeliveryCode = Waybill::$AIR_FREIGHT_CODE;
}
if ($address[$order['recipient_province']] && in_array($order['recipient_city'], $address[$order['recipient_province']], true)) {
$this->timedDeliveryCode = Waybill::$AIR_FREIGHT_CODE;
}
}
}
private function prepareRequest($order, $shopShip)
{
$this->getTimedDelivery($order);
$this->setObjectId();
$items = [];
foreach ($order['items'] as $item) {
if ($item['should_print']) {
// 使用规格编码后删除,此处暂时使用空运名字判断兼容
if (false !== strpos($item['name'], '空运')) {
$this->timedDeliveryCode = Waybill::$AIR_FREIGHT_CODE;
}
$items[] = [
'name' => $item['name'],
'count' => $item['count'],
];
}
}
$senderConfig = [];
foreach ($shopShip->senders as $sender) {
if ($sender['timed_delivery_code'] === $this->timedDeliveryCode) {
$senderConfig = $sender;
break;
}
}
if (empty($senderConfig)) {
abort(404, '发货人信息未匹配');
}
$sender = [
'address' => [
'city' => $senderConfig['city'],
'country' => $senderConfig['country'],
'detail' => $senderConfig['detail'],
'district' => $senderConfig['district'],
'province' => $senderConfig['province'],
],
'name' => $senderConfig['name'],
'mobile' => $senderConfig['mobile'],
];
$orderInfo = [
'logistics_services' => [
'TIMED-DELIVERY' => [
'value' => $this->timedDeliveryCode
],
],
'object_id' => $this->objectId,
'order_info' => [
'order_channels_type' => 'PDD',
'trade_order_list' => [$order['order_sn']],
],
'package_info' => [
'items' => $items,
],
'recipient' => [
'address' => [
'city' => $order['recipient_city'],
'detail' => $order['recipient_detail'],
'district' => $order['recipient_district'],
'province' => $order['recipient_province'],
],
'name' => $order['recipient_name'],
'mobile' => $order['recipient_mobile'],
],
'template_url' => $this->sfOne,
'user_id' => $shopShip->owner_id,
];
return [$sender, $orderInfo, $senderConfig];
}
public function setObjectId()
{
$this->objectId = date('YmdHis') . str_pad(mt_rand(1, 9999999), 7, '0', STR_PAD_LEFT);
return $this;
}
public function setOrders($orders)
{
$orders = $orders->toArray();
// 取出所有组合商品编码
$combinationExternalIds = [];
foreach ($orders as $order) {
foreach ($order['items'] as $item) {
if (false !== strpos($item['external_sku_id'], 'Z')) {
$combinationExternalIds[] = $item['external_sku_id'];
}
}
}
// 获取组合商品详情
$combinations = [];
if ($combinationExternalIds) {
$goodsSkus = GoodsSku::query()
->select(['id', 'external_sku_id'])
->with([
'combinationGoods:id,goods_sku_id,item_id,item_num',
'combinationGoods.goodsSkuItem:id,external_sku_id',
])
->whereIn('external_sku_id', $combinationExternalIds)
->get()
->toArray();
foreach ($goodsSkus as $goodsSku) {
foreach ($goodsSku['combination_goods'] as $item) {
$combinations[$goodsSku['external_sku_id']][] = [
'count' => $item['item_num'],
'external_sku_id' => $item['goods_sku_item']['external_sku_id'],
];
}
}
}
// 订单拆分组合
foreach ($orders as $order) {
$info = [
'id' => $order['id'],
'order_sn' => $order['order_sn'],
'recipient_province' => $order['receiver_address_province'],
'recipient_city' => $order['receiver_address_city'],
'recipient_district' => $order['receiver_address_district'],
'recipient_detail' => $order['receiver_address_detail'],
'recipient_name' => $order['receiver_name'],
'recipient_mobile' => $order['receiver_mobile'],
'participate_no' => $order['is_supplier'] ? $order['participate_no'] : $order['supply_participate_no'],
'note' => $order['business_note'] ? $order['business_note'] . ',' . $order['buyer_memo'] : $order['buyer_memo'],
'items' => []
];
foreach ($order['items'] as $item) {
$count = $item['goods_number'] - $item['already_cancel_number'];
if (isset($combinations[$item['external_sku_id']])) {
$info['items'][] = [
'is_single' => false,
'should_print' => true,
'name' => $item['goods_name'],
'count' => $count,
'external_sku_id' => $item['external_sku_id'],
];
foreach ($combinations[$item['external_sku_id']] as $combinationItem) {
$info['items'][] = [
'is_single' => true,
'should_print' => false,
'count' => $count * $combinationItem['count'],
'external_sku_id' => $combinationItem['external_sku_id'],
];
}
} else {
$info['items'][] = [
'is_single' => true,
'should_print' => true,
'name' => $item['goods_name'],
'count' => $count,
'external_sku_id' => $item['external_sku_id'],
];
}
}
$this->orders[$order['shop_id']][] = $info;
}
return $this;
}
private function getShopShip($shopId)
{
return ShopShip::query()
->select(['id', 'shop_id', 'access_token', 'owner_id'])
->where('shop_id', $shopId)
->with([
'senders' => function ($query) {
$query->where('status', 1)->orderBy('sort');
}
])
->first();
}
}

View File

@ -33,4 +33,9 @@ class FormatUtils
} }
return $data; return $data;
} }
public static function getPercent($dividend, $divisor, $decimal = 2)
{
return bcdiv($dividend, $divisor, $decimal + 2) * 100 . '%';
}
} }

View File

@ -13,7 +13,7 @@ return [
| |
*/ */
'name' => env('APP_NAME', 'CFen Erp'), 'name' => env('APP_NAME', 'chunfen_erp'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -131,7 +131,7 @@ return [
'host' => env('REDIS_HOST', '127.0.0.1'), 'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null), 'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'), 'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'), 'database' => env('REDIS_DB', '10'),
], ],
'cache' => [ 'cache' => [
@ -139,7 +139,7 @@ return [
'host' => env('REDIS_HOST', '127.0.0.1'), 'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null), 'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'), 'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'), 'database' => env('REDIS_CACHE_DB', '11'),
], ],
], ],

View File

@ -24,7 +24,7 @@ class CreateBusinessOrdersTable extends Migration
$table->bigInteger('after_sales_status')->nullable(); $table->bigInteger('after_sales_status')->nullable();
$table->string('business_note')->nullable(); $table->string('business_note')->nullable();
$table->string('buyer_memo')->nullable(); $table->string('buyer_memo')->nullable();
$table->integer('cancel_status')->nullable(); $table->integer('cancel_status')->default(0)->nullable();
$table->bigInteger('confirm_at')->nullable(); $table->bigInteger('confirm_at')->nullable();
$table->bigInteger('discount_amount')->nullable(); $table->bigInteger('discount_amount')->nullable();
$table->string('help_sell_nickname')->nullable(); $table->string('help_sell_nickname')->nullable();
@ -51,7 +51,7 @@ class CreateBusinessOrdersTable extends Migration
$table->string('self_pick_up_site_name')->nullable(); $table->string('self_pick_up_site_name')->nullable();
$table->bigInteger('service_amount')->nullable(); $table->bigInteger('service_amount')->nullable();
$table->bigInteger('shipping_amount')->nullable(); $table->bigInteger('shipping_amount')->nullable();
$table->integer('shipping_status')->nullable(); $table->integer('shipping_status')->default(0)->nullable();
$table->string('supply_activity_no')->nullable(); $table->string('supply_activity_no')->nullable();
$table->integer('supply_participate_no')->nullable(); $table->integer('supply_participate_no')->nullable();
$table->bigInteger('theoretical_refund_amount')->nullable(); $table->bigInteger('theoretical_refund_amount')->nullable();

View File

@ -21,7 +21,7 @@ class CreateBusinessOrderItemsTable extends Migration
$table->bigInteger('shop_id'); $table->bigInteger('shop_id');
$table->bigInteger('business_order_id'); $table->bigInteger('business_order_id');
$table->integer('already_cancel_number')->nullable(); $table->integer('already_cancel_number')->nullable();
$table->integer('cancel_status')->nullable(); $table->integer('cancel_status')->default(0)->nullable();
$table->string('category_name')->nullable(); $table->string('category_name')->nullable();
$table->string('external_sku_id')->nullable(); $table->string('external_sku_id')->nullable();
$table->bigInteger('goods_amount')->nullable(); $table->bigInteger('goods_amount')->nullable();
@ -35,7 +35,7 @@ class CreateBusinessOrderItemsTable extends Migration
$table->bigInteger('help_sell_amount')->nullable(); $table->bigInteger('help_sell_amount')->nullable();
$table->boolean('is_supplier')->nullable(); $table->boolean('is_supplier')->nullable();
$table->integer('need_verification_number')->nullable(); $table->integer('need_verification_number')->nullable();
$table->integer('shipping_status')->nullable(); $table->integer('shipping_status')->default(0)->nullable();
$table->bigInteger('sku_id')->nullable(); $table->bigInteger('sku_id')->nullable();
$table->string('sub_order_sn')->nullable(); $table->string('sub_order_sn')->nullable();
$table->bigInteger('theoretically_refund_amount')->nullable(); $table->bigInteger('theoretically_refund_amount')->nullable();

View File

@ -28,7 +28,7 @@ class CreateGroupsTable extends Migration
$table->string('activity_no')->nullable()->comment('团号'); $table->string('activity_no')->nullable()->comment('团号');
$table->unsignedTinyInteger('create_status')->default(3)->comment('1-创建成功,2-创建失败,3-创建中'); $table->unsignedTinyInteger('create_status')->default(3)->comment('1-创建成功,2-创建失败,3-创建中');
$table->string('error_msg')->nullable()->comment('create_status为2时有创建团失败原因'); $table->string('error_msg')->nullable()->comment('create_status为2时有创建团失败原因');
$table->text('qr_code_url')->nullable()->comment('create_status为1时有团小程序二维码图片地址'); $table->string('qr_code_url')->default('')->comment('create_status为1时有团小程序二维码图片地址');
$table->unsignedBigInteger('create_time')->nullable(); $table->unsignedBigInteger('create_time')->nullable();
$table->unsignedTinyInteger('is_help_sell')->nullable()->comment('是否帮卖0-我发布的,1-我帮卖的'); $table->unsignedTinyInteger('is_help_sell')->nullable()->comment('是否帮卖0-我发布的,1-我帮卖的');
$table->tinyInteger('status')->default(-10)->comment('团状态(-10:待发布/预览中,-5:未开始1:跟团中20:已结束30:已删除'); $table->tinyInteger('status')->default(-10)->comment('团状态(-10:待发布/预览中,-5:未开始1:跟团中20:已结束30:已删除');

View File

@ -0,0 +1,45 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateShopShipsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('shop_ships')) {
return;
}
Schema::create('shop_ships', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('shop_id');
$table->string('access_token');
$table->unsignedBigInteger('expires_at')->comment('access_token过期时间点');
$table->unsignedInteger('expires_in')->comment('access_token过期时间段10表示10秒后过期');
$table->string('owner_id')->comment('商家店铺id');
$table->string('owner_name')->comment('商家账号名称');
$table->string('refresh_token')->comment('refresh token可用来刷新access_token');
$table->unsignedBigInteger('refresh_token_expires_at')->comment('Refresh token过期时间点');
$table->unsignedInteger('refresh_token_expires_in')->comment('refresh_token过期时间段10表示10秒后过期');
$table->text('scope')->comment('接口列表');
$table->tinyInteger('status')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('shop_ships');
}
}

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddGoodsSkuNumToBusinessOrdersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumns('business_orders', ['goods_sku_num', 'print_status'])) {
return;
}
Schema::table('business_orders', function (Blueprint $table) {
$table->integer('goods_sku_num')->default(0)->comment('订单内不同商品种类数量');
$table->tinyInteger('print_status')->default(0)->comment('订单内不同商品种类数量');
$table->index('goods_sku_num');
$table->index('print_status');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('business_orders', function (Blueprint $table) {
$table->dropColumn(['goods_sku_num', 'print_status']);
$table->dropIndex('goods_sku_num');
$table->dropIndex('print_status');
});
}
}

View File

@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateShopSendersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('shop_senders')) {
return;
}
Schema::create('shop_senders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('shop_id');
$table->integer('shop_ship_id');
$table->string('country');
$table->string('province');
$table->string('city');
$table->string('district');
$table->string('detail');
$table->string('name')->nullable()->default('');
$table->string('mobile', 18)->nullable();
$table->integer('sort')->default(1);
$table->integer('status')->default(1);
$table->string('wp_code')->default('');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('shop_senders');
}
}

View File

@ -0,0 +1,74 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWaybillsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('waybills')) {
return;
}
Schema::create('waybills', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('shop_id');
$table->string('request_id')->default('');
$table->text('encryptedData')->nullable();
$table->string('signature')->default('');
$table->string('templateUrl');
$table->string('ver')->default('');
$table->string('waybill_code')->default('');
$table->string('object_id', 32);
$table->integer('key')->default(0);
$table->string('sender_country');
$table->string('sender_province');
$table->string('sender_city');
$table->string('sender_district');
$table->string('sender_detail');
$table->string('sender_name');
$table->integer('sender_mobile');
$table->string('recipient_country')->default('');
$table->string('recipient_province');
$table->string('recipient_city');
$table->string('recipient_district');
$table->string('recipient_detail');
$table->string('recipient_name');
$table->string('recipient_mobile', 16);
$table->string('user_id');
$table->string('wp_code');
$table->string('order_channels_type')->default('PDD');
$table->string('order_sn');
$table->integer('order_id');
$table->integer('participate_no')->default(0);
$table->integer('timed_delivery')->default(247);
$table->string('note')->nullable();
$table->text('items');
$table->tinyInteger('cancel')->default(0);
$table->timestamps();
$table->index('object_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('waybills');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTypeToShopShipsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumns('shop_ships', ['type'])) {
return;
}
Schema::table('shop_ships', function (Blueprint $table) {
$table->string('type')->default('normal')->comment('账户类型normal-普通,air-空运');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('shop_ships', function (Blueprint $table) {
$table->dropColumn(['type']);
});
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddCodeToWaybillsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumns('waybills', ['code'])) {
return;
}
Schema::table('waybills', function (Blueprint $table) {
$table->integer('code')->default(247)->comment('247-电商标快,266-空运');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('waybills', function (Blueprint $table) {
$table->dropColumn(['code']);
});
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTimedDeliveryCodeToShopSendersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumns('shop_senders', ['timed_delivery_code'])) {
return;
}
Schema::table('shop_senders', function (Blueprint $table) {
$table->integer('timed_delivery_code')->default(247)->comment('地址类型: 247-电商标快,266-空运');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('shop_senders', function (Blueprint $table) {
$table->dropColumn(['timed_delivery_code']);
});
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDeveloperConfigsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('developer_configs')) {
return;
}
Schema::create('developer_configs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('key')->comment('配置项名称');
$table->string('value')->comment('配置项值');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('developer_configs');
}
}

View File

@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDailyReportsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('daily_reports')) {
return;
}
Schema::create('daily_reports', function (Blueprint $table) {
$table->bigIncrements('id');
$table->date('date');
$table->unsignedInteger('goods_id');
$table->unsignedInteger('type_id');
$table->unsignedInteger('brand_id');
$table->unsignedInteger('goods_sku_id');
$table->string('external_sku_id', 64);
$table->unsignedInteger('total_goods_price')->default(0);
$table->unsignedInteger('total_goods_cost_price')->default(0);
$table->unsignedInteger('total_goods_amount')->default(0);
$table->unsignedInteger('total_goods_number')->default(0);
$table->unsignedInteger('total_cancel_number')->default(0);
$table->unsignedDecimal('cost')->default(0);
$table->text('shop_data');
$table->timestamps();
$table->index(['date', 'external_sku_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('week_reports');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNameToGoodsSkusTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumn('goods_skus', 'name')) {
return;
}
Schema::table('goods_skus', function (Blueprint $table) {
$table->string('name')->default('')->comment('完整商品名称');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('goods_skus', function (Blueprint $table) {
$table->dropColumn('name');
});
}
}

View File

@ -0,0 +1,60 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDailySalesReportsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('daily_sales_reports')) {
return;
}
Schema::create('daily_sales_reports', function (Blueprint $table) {
$table->bigIncrements('id');
$table->date('date');
$table->unsignedInteger('goods_id');
$table->unsignedInteger('goods_sku_id');
$table->string('name');
$table->string('external_sku_id', 64);
$table->integer('inventory')->default(0)->comment('盘点数量');
$table->integer('arrived_today_num')->default(0)->comment('今日到货');
$table->integer('sales_num')->default(0)->comment('总销量');
$table->integer('already_cancel_number')->default(0)->comment('已取消数量');
$table->unsignedInteger('loss_num')->default(0)->comment('损耗');
$table->decimal('goal_rate', 8, 4)->default(0)->comment('目标去化率');
$table->integer('S1')->default(0)->comment('11-12');
$table->integer('S2')->default(0)->comment('11-13:30');
$table->integer('S3')->default(0)->comment('11-15');
$table->integer('S4')->default(0)->comment('15-16');
$table->integer('S5')->default(0)->comment('11-17:30');
$table->integer('S6')->default(0)->comment('11-20');
$table->integer('S7')->default(0)->comment('11-9:30');
$table->decimal('S1_rate', 8, 4)->default(0);
$table->decimal('S2_rate', 8, 4)->default(0);
$table->decimal('S3_rate', 8, 4)->default(0);
$table->decimal('S4_rate', 8, 4)->default(0);
$table->decimal('S5_rate', 8, 4)->default(0);
$table->decimal('S6_rate', 8, 4)->default(0);
$table->decimal('S7_rate', 8, 4)->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('daily_sales_reports');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddGoalRateToGoodsSkusTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumn('goods_skus', 'goal_rate')) {
return;
}
Schema::table('goods_skus', function (Blueprint $table) {
$table->decimal('goal_rate', 8, 4)->default(0)->comment('目标去化率');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('goods_skus', function (Blueprint $table) {
$table->dropColumn('goal_rate');
});
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddReissueNumToDailyStockRecordsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumn('daily_stock_records', 'reissue_num')) {
return;
}
Schema::table('daily_stock_records', function (Blueprint $table) {
$table->unsignedInteger('reissue_num')->default(0)->comment('补发数量');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('daily_stock_records', function (Blueprint $table) {
$table->dropColumn('reissue_num');
});
}
}

View File

@ -40,8 +40,10 @@ class MenusTableSeeder extends Seeder
['parent_id' => $id, 'code' => 'PLAT_GOODS_LIST', 'name' => '货品列表', 'seq' => 0], ['parent_id' => $id, 'code' => 'PLAT_GOODS_LIST', 'name' => '货品列表', 'seq' => 0],
['parent_id' => $id, 'code' => 'PLAT_ORDER_LIST', 'name' => '订单列表', 'seq' => 1], ['parent_id' => $id, 'code' => 'PLAT_ORDER_LIST', 'name' => '订单列表', 'seq' => 1],
]); ]);
// 团购 // 团购管理
DB::table('menus')->insertGetId(['parent_id' => 0, 'code' => 'GROUP_MANAGEMENT', 'name' => '团购管理', 'seq' => 1]); DB::table('menus')->insertGetId(['parent_id' => 0, 'code' => 'GROUP_MANAGEMENT', 'name' => '团购管理', 'seq' => 2]);
// 数据中心
DB::table('menus')->insertGetId(['parent_id' => 0, 'code' => 'DATA_CENTER', 'name' => '数据中心', 'seq' => 1]);
} }
public function update() public function update()
@ -49,10 +51,23 @@ class MenusTableSeeder extends Seeder
$routes = include(resource_path('lang/zh-CN/permission.php')); $routes = include(resource_path('lang/zh-CN/permission.php'));
foreach ($routes as $code => $route) { foreach ($routes as $code => $route) {
if (false === strpos($code, '.')) { if (false === strpos($code, '.')) {
DB::table('menus')->updateOrInsert( $menu = DB::table('menus')->where('id', $route['id'])->first();
['id' => $route['id'], 'parent_id' => $route['parent_id'], 'code' => $code], if ($menu) {
['name' => $route['name'], 'show' => $route['show']] DB::table('menus')->where('id', $route['id'])->update([
); 'parent_id' => $route['parent_id'],
'code' => $code,
'name' => $route['name'],
'show' => $route['show'],
]);
} else {
DB::table('menus')->insert([
'id' => $route['id'],
'parent_id' => $route['parent_id'],
'code' => $code,
'name' => $route['name'],
'show' => $route['show']
]);
}
} }
} }
} }

1
public/dist/css/149.9f84b84a.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.aside-show[data-v-c25279e8]{transition:all .3s;opacity:0;width:0!important}.aside-hide[data-v-c25279e8]{transition:all .3s;opacity:1;width:200px!important}.el-container[data-v-c25279e8]{height:100vh}.el-aside[data-v-c25279e8]{background-color:#d3dce6;color:#333;overflow-x:hidden}.el-aside[data-v-c25279e8]::-webkit-scrollbar{width:8px}.el-aside[data-v-c25279e8]::-webkit-scrollbar-thumb{background-color:hsla(220,4%,58%,.3);border-radius:20px}.el-main[data-v-c25279e8]{background-color:#f0f2f5;color:#333;padding:0 0!important}.el-main[data-v-c25279e8]::-webkit-scrollbar{width:10px}.el-main[data-v-c25279e8]::-webkit-scrollbar-thumb{background-color:hsla(220,4%,58%,.3)}.box-card[data-v-c25279e8]{min-height:calc(100vh - 120px);margin:10px}.conent[data-v-c25279e8]{width:100%;min-height:calc(100vh - 200px);position:relative}.add[data-v-c25279e8]{cursor:pointer;font-size:25px;color:#606266}.head[data-v-c25279e8]{padding:10px;background-color:#fff;border-bottom:1px solid #f6f6f6;box-shadow:0 1px 4px rgba(0,21,41,.08)}.head ul[data-v-c25279e8]{display:flex;justify-content:space-between}.head ul li[data-v-c25279e8]{display:flex;align-items:center}.head ul li .right[data-v-c25279e8]{margin-left:20px}.head ul li .token[data-v-c25279e8]{cursor:pointer}.el-aside[data-v-c25279e8]{background:#282c34;box-shadow:2px 0 6px rgba(0,21,41,.35)}[data-v-c25279e8] .el-menu{border:none}.el-menu-item[data-v-c25279e8]:hover{outline:0!important;background:#5470c6!important;border-radius:5px!important}.el-menu-item.is-active[data-v-c25279e8]{color:#fff!important;background:#5470c6!important;border-radius:5px!important}.el-menu-item-group__title[data-v-c25279e8]{padding:0 0!important}

View File

@ -1 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}} #nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}

View File

@ -1 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}} #nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}a[data-v-61aedda5]{text-decoration:none;color:#fff}.block[data-v-61aedda5]{margin-top:20px}

View File

@ -1 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}} #nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-679bd5d4]{margin-top:20px}

View File

@ -1 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}} #nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}a[data-v-446dc800]{text-decoration:none;color:#fff}.block[data-v-446dc800]{margin-top:20px}

1
public/dist/css/470.4d13bccb.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.el-upload--picture-card[data-v-1963f826]{width:50px;height:50px}.el-form-item[data-v-1963f826]{margin-left:40px}.avatar-uploader .el-upload[data-v-1963f826]{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-1963f826]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-1963f826]{font-size:28px;color:#8c939d;width:148px;height:148px;line-height:148px;text-align:center}.avatar[data-v-1963f826]{width:148px;height:148px;display:block}

1
public/dist/css/485.8c529968.css vendored Normal file
View File

@ -0,0 +1 @@
.backimg[data-v-53d3caae]{width:100%;height:1080px;background-image:url(../img/%E7%BB%84%2032.a17892e3.png);background-repeat:no-repeat;background-size:100%;position:relative}.sign[data-v-53d3caae]{width:400px;height:500px;position:absolute;top:270px;right:300px}.sign input[data-v-53d3caae]{width:400px;height:51px;border:2px solid #bcbcbc;opacity:1;border-radius:5px;margin-bottom:25px}.sign .title[data-v-53d3caae]{width:125px;height:23px;font-size:22px;font-family:BigruixianBlackGBV1\.0;font-weight:400;line-height:23px;color:#2b53ec;opacity:1}.sign .manage[data-v-53d3caae]{margin-top:19px;margin-bottom:50px}.sign .manage img[data-v-53d3caae]{margin-right:20px}.sign .manage span[data-v-53d3caae]{width:340px;height:57px;font-size:54px;font-family:BigruixianBlackGBV1\.0;font-weight:400;line-height:57px;color:#2b53ec;opacity:1}.sign .title-1[data-v-53d3caae]{width:70px;height:35px;font-size:35px;font-family:Source Han Sans CN;font-weight:500;line-height:60px;color:#393939;opacity:1;margin-bottom:35px}.sign .el-button[data-v-53d3caae]{width:400px;height:58px;background:#2b53ec;border-radius:5px;margin-top:40px}.sign .el-checkbox[data-v-53d3caae]{color:#2b53ec}

1
public/dist/css/507.e6e4e404.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-5c3641da]{margin-top:20px}

1
public/dist/css/523.c2fac020.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-28a560c7]{margin-top:20px}

1
public/dist/css/550.83822904.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-395eec9a]{margin-top:20px}

1
public/dist/css/561.6e3fdab5.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-cd5188f4]{margin-top:20px}

1
public/dist/css/599.70613752.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.el-upload--picture-card[data-v-0509b8a0]{width:50px;height:50px}.avatar-uploader .el-upload[data-v-0509b8a0]{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-0509b8a0]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-0509b8a0]{font-size:28px;color:#8c939d;width:148px;height:148px;line-height:148px;text-align:center}.avatar[data-v-0509b8a0]{width:148px;height:148px;display:block}.shop-sku-img[data-v-0509b8a0]{display:flex}.shop-sku-img .shop[data-v-0509b8a0]{margin-right:20px;margin-bottom:20px}.shop-sku-img .shop .shop-name[data-v-0509b8a0]{text-align:center}

1
public/dist/css/637.5c0a41ae.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}

1
public/dist/css/650.5c0a41ae.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}

1
public/dist/css/674.6e10234a.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.table[data-v-b19d0430]{margin-top:20px;position:relative}.btn[data-v-b19d0430]{float:right}[data-v-b19d0430] .cell{display:flex;align-items:center}.commodityimg[data-v-b19d0430]{width:59px;height:59px;background:hsla(0,0%,89%,.39);opacity:1;display:block;margin-right:12px}.Img[data-v-b19d0430]{width:100%;height:100%}.confirmbtn[data-v-b19d0430]{width:114px;height:44px;border-radius:3px;margin-top:21px;margin-bottom:8px}.import-right[data-v-b19d0430]{margin-top:30px}.import-right a[data-v-b19d0430]{text-decoration:none;color:#000}[data-v-b19d0430] .btn11{padding:0;width:14px;height:14px}[data-v-b19d0430] .btn11 img{width:100%;height:100%}.page[data-v-b19d0430]{margin-top:20px}

1
public/dist/css/679.32fc9cb6.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.table[data-v-5ba5084c]{margin-top:20px;position:relative}.btn[data-v-5ba5084c]{float:right}[data-v-5ba5084c] .cell{display:flex;align-items:center}.commodityimg[data-v-5ba5084c]{width:59px;height:59px;background:hsla(0,0%,89%,.39);opacity:1;display:block;margin-right:12px}.Img[data-v-5ba5084c]{width:100%;height:100%}[data-v-5ba5084c] .btn11{padding:0;width:14px;height:14px}[data-v-5ba5084c] .btn11 img{width:100%;height:100%}.page[data-v-5ba5084c]{margin-top:20px}

1
public/dist/css/750.aa1db5a0.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-7a83ab34]{margin-top:20px}[data-v-7a83ab34] .el-card__body{padding:0}

1
public/dist/css/800.97f84af6.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-891ad2ec]{margin-top:20px}

1
public/dist/css/801.c437d427.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-999e92dc]{margin-top:20px}

1
public/dist/css/931.5c0a41ae.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}

1
public/dist/css/949.3d2ed25d.css vendored Normal file
View File

@ -0,0 +1 @@
#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.block[data-v-26e9f73c]{margin-top:20px}

View File

@ -1 +0,0 @@
.block[data-v-999e92dc]{margin-top:20px}

Some files were not shown because too many files have changed in this diff Show More