Merge pull request !4 from feature
This commit is contained in:
赵世界 2022-08-15 01:52:18 +00:00 committed by Gitee
commit b9cb520740
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
62 changed files with 2880 additions and 1032 deletions

View File

@ -15,7 +15,7 @@
1. `composer install`
2. `cp .env.example .env`
3. 修改 .env 配置项为本地配置
4. 创建数据库 `CREATE DATABASE IF NOT EXISTS `erp` DEFAULT CHARACTER SET utf8;`
4. 创建数据库 `CREATE DATABASE IF NOT EXISTS `erp` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;`
5. `php artisan migrate` 如果数据填充没有执行成功,则需要再次执行 `php artisan migrate:fresh --seed`
6. `php artisan key:generate`
7. `php artisan update:super_admin_permissions` 更新超级管理员角色权限

View File

@ -0,0 +1,56 @@
<?php
namespace App\Console\Commands;
use App\Services\Business\BusinessFactory;
use App\Utils\DateTimeUtils;
use Illuminate\Console\Command;
use App\Models\Shop;
use Swoole\Timer;
use Swoole\Event;
class Swoole extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'swoole:timer';
/**
* 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()
{
Timer::tick(1000, function () {
$shops = Shop::query()->where('plat_id', 1)->where('status', 1)->get();
$endTime = DateTimeUtils::getMicroTime();
$beginTime = $endTime - 10000;
foreach ($shops as $shop) {
BusinessFactory::init()->make($shop->plat_id)->setShop($shop)->downloadOrdersAndSave($beginTime, $endTime, 'increment', 1);
}
});
Event::wait();
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace App\Console\Commands;
use App\Models\Shop;
use App\Services\Business\BusinessFactory;
use App\Utils\DateTimeUtils;
use Illuminate\Console\Command;
class Test extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test';
/**
* 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()
{
$shop = Shop::query()->find(1);
$business = BusinessFactory::init()->make($shop->plat_id);
$business->setShop($shop);
// 下载商品列表
// $business->downloadGoodsListAndBind();
// 下载单个商品
// $business->downloadGoods(1);
// 库存修改
// $business->incrQuantity(1);
// 订单下载
// $beginTime = DateTimeUtils::getMicroTime('2022-08-08');
// $endTime = DateTimeUtils::getMicroTime('2022-08-09');
// $business->downloadOrdersAndSave($beginTime, $endTime);
$this->info('执行测试成功');
}
}

View File

@ -7,7 +7,7 @@ use Illuminate\Console\Command;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class UpdateSuperPermissions extends Command
class UpdateSuperAdminPermissions extends Command
{
/**
* The name and signature of the console command.

View File

@ -2,6 +2,7 @@
namespace App\Console;
use App\Console\Commands\Inventory;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@ -24,8 +25,9 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
// 服务器添加cron入口
// * * * * * cd /home/wwwroot/erp.staging.chutang66.com && php artisan schedule:run >> /dev/null 2>&1
$schedule->command(Inventory::class)->dailyAt('07:00');
}
/**

View File

@ -0,0 +1,58 @@
<?php
namespace App\Events;
use App\Models\BusinessOrderItem;
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 BusinessOrdersUpdate
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $num;
public $businessOrderItem;
public $goodsSku;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(BusinessOrderItem $item, $num)
{
$this->businessOrderItem = $item;
$this->num = $num;
$this->updateStock();
}
private function updateStock()
{
[$goodsCode, $skuCode] = explode('_', $this->businessOrderItem->external_sku_id);
$this->goodsSku = GoodsSku::query()->where('sku_code', $skuCode)
->with(['goods' => function ($query) use ($goodsCode) {
$query->where('goods_code', $goodsCode);
}])
->first();
if ($this->goodsSku) {
$this->goodsSku->stock += $this->num;
$this->goodsSku->save();
}
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -52,8 +52,8 @@ class RegisterController extends Controller
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'unique:users', 'max:255'],
'email' => ['string', 'email', 'max:255', 'unique:users'],
'name' => ['required', 'string', 'unique:users', 'max:191'],
'email' => ['string', 'email', 'max:191', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'role_id' => ['required', 'numeric', 'exists:roles,id'],
]);

View File

@ -32,7 +32,7 @@ class GoodsBrandsController extends Controller
{
$validator = Validator::make($request->all(), [
'names' => 'required|array',
'names.*' => 'required|string|max:255|unique:goods_brands,name',
'names.*' => 'required|string|max:191|unique:goods_brands,name',
]);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
@ -68,7 +68,7 @@ class GoodsBrandsController extends Controller
'name' => [
'required',
'string',
'max:255',
'max:191',
Rule::unique('goods_brands')->ignore($id),
]
]);

View File

@ -6,7 +6,7 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\GoodsSkuRequest;
use App\Http\Resources\GoodsResource;
use App\Models\Log as LogModel;
use App\Utils\FormatUtils;
use App\Utils\DateTimeUtils;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
@ -59,6 +59,7 @@ class GoodsController extends Controller
$goodsSkus = [];
foreach ($request->skus as $item) {
$item['goods_id'] = $goods->id;
$item['stock'] = $item['num'];
$item['reference_price'] = $item['cost'] * 1.5;
$goodsSkus[] = $item;
}
@ -69,7 +70,7 @@ class GoodsController extends Controller
foreach ($collection as $sku) {
$newRecords[] = [
'sku_id' => $sku['id'],
'day' => FormatUtils::date(),
'day' => DateTimeUtils::getToday(),
];
}
$record = new DailyStockRecord();
@ -89,6 +90,11 @@ class GoodsController extends Controller
public function download()
{
return Storage::download(resource_path('templates/goods_skus_import.xlsx'));
$file = resource_path('templates/goods_skus_import.xlsx');
$headers = [
'Content-Type: application/xlsx',
];
return response()->download($file, 'goods_skus_import.xlsx', $headers);
}
}

View File

@ -9,7 +9,7 @@ use App\Http\Requests\GoodsSkuRequest;
use App\Models\Goods;
use App\Models\Log;
use App\Models\Log as LogModel;
use App\Utils\FormatUtils;
use App\Utils\DateTimeUtils;
use Illuminate\Http\Request;
use App\Models\GoodsSku;
use App\Http\Resources\GoodsSkuResource;
@ -45,7 +45,7 @@ class GoodsSkusController extends Controller
->pluck('sku_id')
->toArray();
}
$day = FormatUtils::date();
$day = DateTimeUtils::getToday();
$goodsSkus = GoodsSku::query()
->whereIn('goods_id', $goodsIds)
->when($ids, function ($query, $ids) {
@ -56,8 +56,9 @@ class GoodsSkusController extends Controller
$query->with(['type:id,name', 'brand:id,name']);
}])
->with(['daily' => function ($query) use ($day) {
return $query->where('day', $day)->first(['id', 'sku_id', 'day', 'arrived_today_num', 'loss_num', 'inventory']);
$query->where('day', $day);
}])
->orderBy('updated_at', 'desc')
->paginate();
return GoodsSkuResource::collection($goodsSkus);
@ -92,10 +93,15 @@ class GoodsSkusController extends Controller
$this->addLog($id, 'update');
// 商品更新
$goods = Goods::query()->find($sku->goods_id);
$this->log = new LogModel([
'module' => 'goods',
'action' => $request->getMethod(),
'target_type' => 'goods',
]);
$this->setBeforeUpdate($goods->toArray());
$goods->update($request->goods);
$this->setAfterUpdate($goods->toArray());
$this->addLog($sku->goods_id, 'update', 'goods');
$this->addLog($sku->goods_id, 'update');
DB::commit();
} catch (\Exception $exception) {
DB::rollBack();
@ -119,7 +125,8 @@ class GoodsSkusController extends Controller
Rule::exists('goods_skus', 'id'),
],
];
$validator = $this->validateUpdate($request->all(), $appendRules);
$skuRules = (new GoodsSkuRequest())->arrayRules('skus.*.');
$validator = Validator::make($request->all(), array_merge($appendRules, $skuRules));
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
@ -155,7 +162,7 @@ class GoodsSkusController extends Controller
$costLog['after_update'] = $goodsSku->cost;
$logs[] = $costLog;
// 今日到货
$record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', FormatUtils::date())->first(['id', 'arrived_today_num']);
$record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', DateTimeUtils::getToday())->first(['id', 'arrived_today_num']);
$arrivedLog['target_field'] = 'arrived_today_num';
$arrivedLog['before_update'] = $record->arrived_today_num;
$record->arrived_today_num += $sku['arrived_today_num'];
@ -191,8 +198,7 @@ class GoodsSkusController extends Controller
'target_id' => $sku['id'],
'user_id' => $request->user()->id
];
// 今日到货
$record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', FormatUtils::date())->first(['id', 'inventory']);
$record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', DateTimeUtils::getToday())->first(['id', 'inventory']);
$inventoryLog['target_field'] = 'inventory';
$inventoryLog['before_update'] = $record->inventory;
$record->inventory = $sku['inventory'];
@ -215,25 +221,29 @@ class GoodsSkusController extends Controller
return response($this->res, $this->res['httpCode']);
}
private function stock($skus)
private function stock($request)
{
$skus = $request->skus;
$update = reset($skus);
DB::beginTransaction();
try {
$sku = GoodsSku::query()->where('id', $update['id'])->get(['id', 'two_days_ago_num', 'yesterday_num', 'num', 'stock']);
$sku = GoodsSku::query()->where('id', $update['id'])->first(['id', 'two_days_ago_num', 'yesterday_num', 'num', 'stock', 'reserve']);
$record = DailyStockRecord::query()
->where('sku_id', $sku->id)
->where('day', FormatUtils::date())
->where('day', DateTimeUtils::getToday())
->first();
$this->setBeforeUpdate([
'two_days_ago_num' => $sku->two_days_ago_num,
'yesterday_num' => $sku->yesterday_num,
'num' => $sku->num,
'arrived_today_num' => $record->arrived_today_num,
'num' => $sku->num,
'stock' => $sku->stock,
]);
$sku->two_days_ago_num = $update['two_days_ago_num'];
$sku->yesterday_num = $update['yesterday_num'];
$sku->num = $update['two_days_ago_num'] + $update['yesterday_num'] + $sku->stock;
$stock = $update['two_days_ago_num'] + $update['yesterday_num'] + $update['arrived_today_num'] - $sku->reserve - $record->loss_num;
$sku->stock = $stock;
$sku->num = $stock + $sku->reserve + $record->loss_num;
$sku->save();
$record->arrived_today_num = $update['arrived_today_num'];
$record->save();
@ -241,6 +251,8 @@ class GoodsSkusController extends Controller
'two_days_ago_num' => $sku->two_days_ago_num,
'yesterday_num' => $sku->yesterday_num,
'arrived_today_num' => $record->arrived_today_num,
'num' => $sku->num,
'stock' => $sku->stock,
]);
$this->addLog($sku->id, 'stock');
DB::commit();
@ -290,33 +302,37 @@ class GoodsSkusController extends Controller
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
return response($this->res, $this->res['httpCode']);
goto end;
}
$updateField = \request('updateField');
$sku = GoodsSku::query()->find($id);
if ('loss_num' === $updateField) {
$model = DailyStockRecord::query()
$record = DailyStockRecord::query()
->where('sku_id', $id)
->where('day', FormatUtils::date())
->where('day', DateTimeUtils::getToday())
->first(['id', 'loss_num']);
$this->log->message = $request->get('reason');
$this->setBeforeUpdate($model->loss_num);
$model->loss_num += $request->loss_num;
$this->setBeforeUpdate($record->loss_num);
$record->loss_num += $request->loss_num;
$record->save();
$this->setAfterUpdate($record->loss_num);
$sku->stock -= $request->loss_num;
} else {
$model = GoodsSku::query()->find($id);
$this->setBeforeUpdate($model->$updateField);
$sku->$updateField = $request->$updateField;
}
$this->setBeforeUpdate($sku->$updateField);
if ('reserve' === $updateField) {
$changeNum = $model->reserve - $request->reserve;
$model->stock += $changeNum;
$model->reserve = $request->reserve;
$model->num += $changeNum;
} else {
$model->$updateField = $request->$updateField;
$changeNum = $sku->reserve - $request->reserve;
if (0 > $changeNum + $sku->stock) {
$this->setValidatorFailResponse('预留量超过库存数量');
goto end;
}
$sku->stock += $changeNum;
}
$model->save();
$this->setAfterUpdate($model->$updateField);
$sku->save();
$this->setAfterUpdate($sku->$updateField);
$this->addLog($id, $updateField);
end:
return response($this->res, $this->res['httpCode']);
}
@ -331,8 +347,9 @@ class GoodsSkusController extends Controller
];
}
try {
$collection = Excel::import(new GoodsSkusImport(), $request->file('goodsSkus'));
$this->setAfterUpdate($collection->toArray());
$import = new GoodsSkusImport();
$path = $request->file('goodsSkus');
Excel::import($import, $path);
$this->addLog(0, 'import');
} catch (ValidationException $exception) {
$this->setValidatorFailResponse($exception->validator->getMessageBag()->getMessages());

View File

@ -32,7 +32,7 @@ class GoodsTypesController extends Controller
{
$validator = Validator::make($request->all(), [
'names' => 'required|array',
'names.*' => 'required|string|max:255|unique:goods_types,name',
'names.*' => 'required|string|max:191|unique:goods_types,name',
]);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
@ -68,7 +68,7 @@ class GoodsTypesController extends Controller
'name' => [
'required',
'string',
'max:255',
'max:191',
Rule::unique('goods_types')->ignore($id),
]
]);

View File

@ -12,6 +12,7 @@ class LogsController extends Controller
public function index(Request $request)
{
$res = Log::query()
->orderBy('id', 'desc')
->with(['user:id,name'])
->filter()
->paginate();

View File

@ -6,8 +6,6 @@ use App\Http\Controllers\Controller;
use App\Models\Log as LogModel;
use App\Models\Menu;
use App\Http\Resources\MenusResource;
use App\Models\User;
use App\Utils\ArrayUtils;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

View File

@ -39,7 +39,7 @@ class PermissionsController extends Controller
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255|unique:permissions,name',
'name' => 'required|string|max:191|unique:permissions,name',
]);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
@ -63,7 +63,7 @@ class PermissionsController extends Controller
public function update($id, Request $request)
{
$validator = Validator::make($request->all(), [
'name' => ['required', 'string', 'max:255', Rule::unique('permissions')->ignore($id),]
'name' => ['required', 'string', 'max:191', Rule::unique('permissions')->ignore($id),]
]);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());

View File

@ -24,7 +24,7 @@ class RolesController extends Controller
public function index()
{
$roles = Role::query()->with('permissions')->get()->toArray();
$roles = Role::query()->with('permissions')->where('id', '<>', 1)->get()->toArray();
$routes = include(resource_path('lang/zh-CN/permission.php'));
foreach ($roles as &$role) {
$permissions = [];
@ -40,7 +40,7 @@ class RolesController extends Controller
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255|unique:roles,name',
'name' => 'required|string|max:191|unique:roles,name',
]);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
@ -75,7 +75,7 @@ class RolesController extends Controller
public function update($id, Request $request)
{
$validator = Validator::make($request->all(), [
'name' => ['required', 'string', 'max:255', Rule::unique('roles')->ignore($id),]
'name' => ['required', 'string', 'max:191', Rule::unique('roles')->ignore($id),]
]);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());

View File

@ -3,14 +3,14 @@
namespace App\Http\Controllers\Shop;
use App\Http\Controllers\Controller;
use App\Listeners\BindBusinessGoods;
use App\Listeners\UpdateBusinessGoodsStock;
use App\Models\Shop;
use App\Http\Resources\ShopsResource;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use App\Services\Business\BusinessFactory;
use Illuminate\Validation\Rule;
use App\Models\BusinessOrderItem;
class ShopsController extends Controller
{
@ -37,7 +37,7 @@ class ShopsController extends Controller
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255|unique:shops,name',
'name' => 'required|string|max:191|unique:shops,name',
'plat_id' => 'required|integer',
]);
if ($validator->fails()) {
@ -48,6 +48,9 @@ class ShopsController extends Controller
$shop = new Shop();
$shop->name = $request->name;
$shop->plat_id = $request->plat_id;
if (0 == $request->plat_id) {
$shop->status = 2;
}
$shop->save();
return response($this->res, $this->res['httpCode']);
@ -60,7 +63,7 @@ class ShopsController extends Controller
$platList = $shop->getPlatList();
$shop = $shop->find($shopId);
if ($platList[$platId] === $shop->plat_id) {
BusinessFactory::init()->make($shop->plat_id)->authCallback($request->get('code'), $shop->id);
BusinessFactory::init()->make($shop->plat_id)->authCallback($request->get('code'), $shop);
} else {
$this->res = [
'httpCode' => 403,
@ -89,11 +92,43 @@ class ShopsController extends Controller
$business->setShop($shop);
if ('goods' === $request->get('type')) {
$business->bindGoods($request->get('data'));
event(new BindBusinessGoods($shop));
}
if ('orders' === $request->get('type')) {
$business->saveOrders();
event(new UpdateBusinessGoodsStock($shop));
}
$business->saveOrders($request->get('data'));
}
return response(['Code' => 10000, 'Message' => 'SUCCESS']);
}
public function countOrdersNumWithSkuCode(Request $request)
{
$validator = Validator::make($request->all(), [
'sku_code' => ['required', 'array'],
]);
if ($validator->fails()) {
$this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
return response($this->res, $this->res['httpCode']);
}
$fields = implode(',', [
'shop_id',
'external_sku_id',
'count(id) as count',
]);
$res = BusinessOrderItem::query()
->select(DB::raw($fields))
->whereIn('external_sku_id', $request->get('sku_code'))
->groupBy(['shop_id', 'external_sku_id'])
->with(['shop:id,name'])
->get();
$data = [];
foreach ($res as $item) {
$data[$item->external_sku_id][] = [
'shop_name' => $item->shop->name,
'count' => $item->count,
];
}
return $data;
}
}

View File

@ -33,7 +33,7 @@ class UsersController extends Controller
public function store(Request $request, Faker $faker)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255|unique:users,name',
'name' => 'required|string|max:191|unique:users,name',
'password' => 'required|string|min:8|confirmed',
'email' => 'email',
'role_name' => 'required|string|exists:roles,name'
@ -67,7 +67,7 @@ class UsersController extends Controller
'name' => [
'required',
'string',
'max:255',
'max:191',
Rule::unique('users')->ignore($id),
],
// 'old_password' => 'sometimes|required|string|min:8',

View File

@ -26,8 +26,8 @@ class GoodsRequest extends FormRequest
{
return [
'id' => ['sometimes', 'required', 'integer', 'exists:goods,id'],
'title' => ['required', 'string', 'max:255'],
'img_url' => ['required', 'string', 'max:255'],
'title' => ['required', 'string', 'max:191'],
'img_url' => ['required', 'string', 'max:191'],
'type_id' => ['required', 'integer', 'exists:goods_types,id'],
'brand_id' => ['integer', 'exists:goods_brands,id'],
'goods_code' => ['required', 'alpha_dash', 'max:32', Rule::unique('goods')->ignore(request('goods_id'))],

View File

@ -27,11 +27,11 @@ class GoodsSkuRequest extends FormRequest
return [
'id' => ['sometimes', 'required', 'integer', 'exists:goods_skus,id'],
'goods_id' => ['sometimes', 'required', 'integer', 'exists:goods,id'],
'title' => ['required', 'string', 'max:255'],
'sku_code' => ['required', 'distinct', 'alpha_dash', 'max:32'],
'status' => ['required', 'integer', Rule::in([0, 1, 2])],
'num' => ['required', 'integer'],
'cost' => ['required', 'numeric'],
'title' => ['sometimes', 'required', 'string', 'max:191'],
'sku_code' => ['sometimes', 'required', 'distinct', 'alpha_dash', 'max:32'],
'status' => ['sometimes', 'required', 'integer', Rule::in([0, 1, 2])],
'num' => ['sometimes', 'required', 'integer'],
'cost' => ['sometimes', 'required', 'numeric', 'gt:0'],
'reference_price' => [
'sometimes',
'numeric',
@ -45,6 +45,18 @@ class GoodsSkuRequest extends FormRequest
'sometimes',
'integer',
],
'arrived_today_num' => [
'sometimes',
'integer',
],
'yesterday_num' => [
'sometimes',
'integer',
],
'two_days_ago_num' => [
'sometimes',
'integer',
],
'inventory' => [
'sometimes',
'integer',

View File

@ -2,11 +2,14 @@
namespace App\Imports;
use App\Models\DailyStockRecord;
use App\Models\Goods;
use App\Models\GoodsBrand;
use App\Models\GoodsSku;
use App\Models\GoodsType;
use App\Utils\DateTimeUtils;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
@ -30,11 +33,11 @@ class GoodsSkusImport implements ToCollection, SkipsEmptyRows
unset($collection[0], $collection[1]);
$arr = $collection->toArray();
$validator = Validator::make($arr, [
'*.0' => ['required', 'string', 'max:255'],
'*.1' => ['required', 'string', 'max:255', 'exists:goods_types,name'],
'*.2' => ['string', 'max:255', 'exists:goods_brands,name'],
'*.3' => ['required', 'alpha_dash', 'max:32', 'unique:goods,goods_code'],
'*.4' => ['required', 'string', 'max:255'],
'*.0' => ['required', 'string', 'max:191'],
'*.1' => ['required', 'string', 'max:191', 'exists:goods_types,name'],
'*.2' => ['string', 'max:191', 'exists:goods_brands,name'],
'*.3' => ['required', 'alpha_dash', 'max:32'],
'*.4' => ['required', 'string', 'max:191'],
'*.5' => ['required', 'distinct', 'alpha_dash', 'max:32'],
'*.6' => ['required', 'string', Rule::in(['下架', '在售', '预警'])],
'*.7' => ['required', 'max:10'],
@ -76,16 +79,33 @@ class GoodsSkusImport implements ToCollection, SkipsEmptyRows
}
$skus[] = $sku;
}
DB::beginTransaction();
try {
if ($newGoods) {
$goods = new Goods();
$goods->batchInsert(array_values($newGoods));
$hasGoods = Goods::query()->whereIn('goods_code', array_column($newGoods, 'goods_code'))->get(['id', 'goods_code'])->toArray();
$hasGoods = ArrayUtils::index($hasGoods, 'goods_code');
foreach ($skus as &$sku) {
$sku['goods_id'] = isset($hasGoods[$sku['goods_id']]) ? $hasGoods[$sku['goods_id']]['id'] : $sku['goods_id'];
foreach ($skus as &$newGoodsSku) {
$newGoodsSku['goods_id'] = isset($hasGoods[$newGoodsSku['goods_id']]) ? $hasGoods[$newGoodsSku['goods_id']]['id'] : $newGoodsSku['goods_id'];
}
unset($newGoodsSku);
}
$goodsSku = new GoodsSku();
$goodsSku->batchInsert(array_values($skus));
$collection = GoodsSku::query()->whereIn('sku_code', array_column($skus, 'sku_code'))->get(['id', 'sku_code'])->toArray();
$newRecords = [];
foreach ($collection as $sku) {
$newRecords[] = [
'sku_id' => $sku['id'],
'day' => DateTimeUtils::getToday(),
];
}
$record = new DailyStockRecord();
$record->batchInsert($newRecords);
DB::commit();
} catch (\Exception $exception) {
DB::rollBack();
}
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Jobs;
use App\Models\BusinessOrderItem;
use App\Models\GoodsSku;
use App\Services\Business\BusinessFactory;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class BusinessGoodsSkuIncrQuantity implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $shop;
public $businessOrderItem;
public $num;
public $goodsSku;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($shop, BusinessOrderItem $businessOrderItem, $num, GoodsSku $goodsSku)
{
$this->shop = $shop;
$this->businessOrderItem = $businessOrderItem;
$this->num = $num;
$this->goodsSku = $goodsSku;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
BusinessFactory::init()->make($this->shop['plat_id'])->setShopWithId($this->shop['id'])->incrQuantity($this->businessOrderItem, $this->num, true, $this->goodsSku);
}
}

View File

@ -2,12 +2,14 @@
namespace App\Listeners;
use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use App\Events\BusinessOrdersUpdate;
class SendDatabaseNotification
class SendDatabaseNotification implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
@ -21,11 +23,13 @@ class SendDatabaseNotification
/**
* Handle the event.
*
* @param Registered $event
* @param BusinessOrdersUpdate $event
* @return void
*/
public function handle(Registered $event)
public function handle(BusinessOrdersUpdate $event)
{
//
if (5 >= $event->goodsSku->stock) {
// 发送通知给管理员
}
}
}

View File

@ -2,33 +2,55 @@
namespace App\Listeners;
use App\Models\Log;
use App\Models\Shop;
use Illuminate\Auth\Events\Registered;
use App\Services\Business\BusinessFactory;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use App\Events\BusinessOrdersUpdate;
use App\Jobs\BusinessGoodsSkuIncrQuantity;
class UpdateBusinessGoodsStock
class UpdateBusinessGoodsStock implements ShouldQueue
{
protected $shop;
use InteractsWithQueue;
/**
* Create the event listener.
*
* @return void
*/
public function __construct(Shop $shop)
public function __construct()
{
$this->shop = $shop;
//
}
/**
* Handle the event.
*
* @param Registered $event
* @param BusinessOrdersUpdate $event
* @return void
*/
public function handle(Registered $event)
public function handle(BusinessOrdersUpdate $event)
{
//
if (empty($event->goodsSku)) {
$log = new Log();
$log->module = 'goods';
$log->action = 'PATCH';
$log->target_type = 'goods_sku';
$log->target_id = $event->goodsSku->id;
$log->target_field = 'stock';
$log->user_id = $event->businessOrderItem->shop_id;
$log->message = ($event->businessOrderItem->external_sku_id ?: '商品') . '未找到';
$log->save();
return;
}
$shops = Shop::query()->where('id', '<>', $event->businessOrderItem->shop_id)->where('status', 1)->get(['id', 'plat_id']);
if (empty($shops)) {
return;
}
foreach ($shops as $shop) {
BusinessGoodsSkuIncrQuantity::dispatch($shop, $event->businessOrderItem, $event->num, $event->goodsSku);
}
}
}

View File

@ -9,5 +9,29 @@ class BusinessGoodsSku extends Model
*
* @var array
*/
protected $guarded = [];
protected $fillable = [
'shop_id',
'business_order_id',
'already_cancel_number',
'cancel_status',
'category_name',
'external_sku_id',
'goods_amount',
'goods_cost_price',
'goods_id',
'goods_name',
'goods_number',
'goods_price',
'goods_purchase_price',
'goods_specification',
'help_sell_amount',
'is_supplier',
'need_verification_number',
'shipping_status',
'sku_id',
'sub_order_sn',
'theoretically_refund_amount',
'thumb_url',
'verification_number',
];
}

View File

@ -9,5 +9,45 @@ class BusinessOrder extends Model
*
* @var array
*/
protected $guarded = [];
protected $fillable = [
'shop_id',
'receiver_address_detail',
'receiver_address_province',
'self_pick_site_no',
'discount_amount',
'theoretical_refund_amount',
'receiver_address_district',
'verification_status',
'inner_transaction_id',
'is_supplier',
'service_amount',
'supply_participate_no',
'updated_at',
'order_amount',
'receiver_address_city',
'receiver_name',
'business_note',
'buyer_memo',
'logistics_type',
'help_sell_nickname',
'activity_title',
'after_sales_status',
'mall_activity_type',
'transaction_id',
'activity_no',
'confirm_at',
'platform_discount_amount',
'participate_no',
'receiver_mobile',
'shipping_status',
'shipping_amount',
'cancel_status',
'nick_name',
'order_sn',
];
public function items()
{
return $this->hasMany(BusinessOrderItem::class, 'business_order_id');
}
}

View File

@ -9,10 +9,39 @@ class BusinessOrderItem extends Model
*
* @var array
*/
protected $guarded = [];
protected $fillable = [
'shop_id',
'business_order_id',
'already_cancel_number',
'cancel_status',
'category_name',
'external_sku_id',
'goods_amount',
'goods_cost_price',
'goods_id',
'goods_name',
'goods_number',
'goods_price',
'goods_purchase_price',
'goods_specification',
'help_sell_amount',
'is_supplier',
'need_verification_number',
'shipping_status',
'sku_id',
'sub_order_sn',
'theoretically_refund_amount',
'thumb_url',
'verification_number',
];
public function order()
{
return $this->hasOne(BusinessOrder::class, 'id', 'business_order_id');
}
public function shop()
{
return $this->hasOne(Shop::class, 'id', 'shop_id');
}
}

View File

@ -4,5 +4,5 @@ namespace App\Models;
class DailyStockRecord extends Model
{
protected $hidden = ['created_at', 'updated_at'];
}

View File

@ -15,7 +15,13 @@ class Goods extends Model
'brand_id',
];
protected $guarded = [];
protected $fillable = [
'title',
'img_url',
'type_id',
'brand_id',
'goods_code',
];
/**
* 多规格

View File

@ -19,7 +19,19 @@ class GoodsSku extends Model
*
* @var array
*/
protected $guarded = [];
protected $fillable = [
'goods_id',
'title',
'sku_code',
'status',
'num',
'stock',
'cost',
'two_days_ago_num',
'yesterday_num',
'reference_price',
'reserve',
];
protected $hidden = ['created_at'];

View File

@ -17,7 +17,8 @@ class Log extends Model
'target_id',
'target_field',
'user_id',
'created_at',
'start_time',
'end_time',
];
public $fillable = [
@ -42,7 +43,7 @@ class Log extends Model
'file' => '文件',
];
return $map[$value];
return $map[$value] ?? $value;
}
public function getActionAttribute($value)
@ -54,7 +55,7 @@ class Log extends Model
'DELETE' => '删除',
];
return $map[$value];
return $map[$value] ?? $value;
}
public function getTargetTypeAttribute($value)
@ -70,9 +71,10 @@ class Log extends Model
'upload' => '上传',
'kuaituantuan' => '快团团',
'miaoxuan' => '妙选',
'goods' => '商品',
];
return $map[$value];
return $map[$value] ?? $value;
}
public function getTargetFieldAttribute($value)
@ -90,7 +92,7 @@ class Log extends Model
'inventory' => '库存盘点',
];
return $map[$value];
return $map[$value] ?? $value;
}
public function setUserIdAttribute($value)

View File

@ -8,7 +8,6 @@ class Shop extends Model
{
protected $hidden = [
'access_token',
'expires_at',
'expires_in',
'refresh_token',
'refresh_token_expires_at',
@ -16,7 +15,9 @@ class Shop extends Model
'pop_auth_token_create_response',
];
protected $guarded = [];
protected $fillable = [
'access_token', 'expires_at', 'expires_in', 'owner_id', 'owner_name', 'refresh_token', 'refresh_token_expires_at', 'refresh_token_expires_in', 'scope', 'pop_auth_token_create_response', 'status'
];
public function getStatusAttribute($value)
{
@ -26,6 +27,9 @@ class Shop extends Model
2 => '无需授权',
3 => '停用',
];
if (1 === (int)$value && ($this->attributes['expires_at'] - time()) / 3600 <= 72) {
return '重新授权';
}
return $map[$value];
}

View File

@ -2,12 +2,9 @@
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use App\Listeners\SendDatabaseNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use App\Listeners\SendDatabaseNotification;
use App\Listeners\BindBusinessGoods;
use App\Listeners\UpdateBusinessGoodsStock;
class EventServiceProvider extends ServiceProvider
@ -18,11 +15,9 @@ class EventServiceProvider extends ServiceProvider
* @var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
SendDatabaseNotification::class,
BindBusinessGoods::class,
'App\Events\BusinessOrdersUpdate' => [
UpdateBusinessGoodsStock::class,
SendDatabaseNotification::class,
],
];

View File

@ -1,27 +1,81 @@
<?php
namespace App\Services\Business;
use App\Events\BusinessOrdersUpdate;
use App\Models\BusinessGoodsSku;
use App\Models\BusinessOrder;
use App\Models\BusinessOrderItem;
use App\Models\GoodsSku;
use App\Models\Log;
use App\Models\Shop;
use Facade\FlareClient\Http\Client;
use GuzzleHttp\Client;
abstract class BusinessClient
{
protected $redirectUri = 'http://erp.chutang66.com/callback';
protected $code;
protected $shop;
protected $skuId = 0;
abstract public function auth();
abstract public function downloadGoods();
abstract public function downloadGoodsListAndBind();
abstract public function downloadGoods($skuId);
abstract public function bindGoods($goods);
abstract public function incrQuantity($skuId);
abstract public function incrQuantity(BusinessGoodsSku $businessGoodsSku, $num, $incremental, GoodsSku $goodsSku);
abstract public function downloadOrders($beginTime, $endTime, $activityNo, $downloadType = 'default');
abstract public function downloadOrdersAndSave($beginTime, $endTime, $downloadType = 'default', $page = 1);
abstract public function saveOrders($orders);
public function saveOrders($orders)
{
$shopId = $this->getShop()->id;
foreach ($orders as $order) {
unset($order['custom_item_list'], $order['logistics_list'], $order['gift_order_list']);
$order['shop_id'] = $shopId;
$orderRecord = BusinessOrder::firstOrNew(['shop_id' => $shopId, 'order_sn' => $order['order_sn']], $order);
if (empty($orderRecord->id)) {
$orderRecord->save();
} else {
$orderRecord->update($order);
}
foreach ($order['sub_order_list'] as $item) {
$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);
$num = 0;
if (empty($orderItem->id)) {
if ($item['cancel_status']) {
if ($num = $item['goods_number'] - $item['already_cancel_number']) {
// 扣库存 $reduceNum
$num = 0 - $num;
}
} else {
// 扣库存
$num = 0 - $item['goods_number'];
}
$orderItem->save();
} else {
if ($item['cancel_status'] !== $orderItem->cancel_status) {
if ($item['cancel_status']) {
// 加库存
$num = $item['already_cancel_number'];
} else {
// 扣库存
$num = 0 - $item['goods_number'];
}
}
$orderItem->update($item);
}
// 增量更新库存
if ($num) {
event(new BusinessOrdersUpdate($orderItem, $num));
}
}
}
}
public function authCallback($code, $shopId)
{
@ -51,39 +105,55 @@ abstract class BusinessClient
return $this->shop;
}
protected function setCode($code)
public function setCode($code)
{
$this->code = $code;
return $this;
}
protected function getCode()
public function getCode()
{
return $this->code;
}
public function setSkuId($skuId)
{
$this->skuId = $skuId;
return $this;
}
public function getSkuId()
{
return $this->skuId;
}
public function formDataPostRequest($url, $params)
{
$method = 'POST';
$headers = [
'headers' => ['Content-type' => 'multipart/form-data;charset=UTF-8'],
'headers' => ['Content-type' => 'application/x-www-form-urlencoded;charset=UTF-8'],
'form_params' => $params
];
$res = (new Client())->makeCurlRequest('POST', $url, $headers);
$res = (new Client())->request($method, $url, $headers);
$size = $res->getBody()->getSize();
$res = json_decode($res->getBody()->getContents(), true);
if (isset($res['error_response'])) {
if ('pdd.ktt.increment.order.query' === $params['type']) {
$log = Log::query()->where('user_id', $this->getShop()->id)->where('target_field', $params['type'])->first();
} else {
$log = new Log();
$log->module = 'plat';
$log->action = 'POST';
$log->target_type = 'kuaituantuan';
$log->target_id = 0;
$log->target_field = '更新库存';
$log->user_id = 1;
$log->message = json_encode($res, 256);
$log->save();
throw new \Exception($res['error_response']['error_msg'], $res['error_response']['error_code']);
}
$log->module = 'plat';
$log->action = $method;
$log->target_type = $this->getShop()->plat_id;
$log->target_id = $this->getSkuId();
$log->target_field = $params['type'];
$log->user_id = $this->getShop()->id;
if ($size < 64000) {
$log->message = json_encode($res, 256);
}
$log->save();
return $res;
}

View File

@ -3,6 +3,7 @@
namespace App\Services\Business;
use App\Services\Business\KuaiTuanTuan\KuaiTuanTuan;
use App\Services\Business\MiaoXuan\MiaoXuan;
class BusinessFactory
{
@ -11,6 +12,7 @@ class BusinessFactory
public function __construct()
{
$this->platList['快团团'] = KuaiTuanTuan::class;
$this->platList['妙选'] = MiaoXuan::class;
}
public function make($platName)

View File

@ -10,7 +10,7 @@ class Goods
{
$type = 'pdd.ktt.goods.query.list';
$appendParams = [
'activity_no' => $activityNo, // 非必填,团号(团号和创建时间只能传一个)
// 'activity_no' => $activityNo, // 非必填,团号(团号和创建时间只能传一个)
'page' => $page,
'size' => $size,
// 'update_time_end' => '', // 非必填,结束最后更新时间(毫秒级时间戳)
@ -27,6 +27,7 @@ class Goods
foreach ($goodsList as $businessGood) {
$skuList = $businessGood['sku_list'];
unset($businessGood['sku_list']);
$businessGood['goods_image_list'] = json_encode($businessGood['goods_image_list'], 256);
foreach ($skuList as $sku) {
$sku['spec_list'] = json_encode($sku['spec_list'], 256);
$data = array_merge($businessGood, $sku);
@ -38,12 +39,16 @@ class Goods
}
}
public function downloadSingle($goodsId)
public static function downloadSingle($goodsId)
{
$type = 'pdd.ktt.goods.query.single';
$appendParams = [
'goods_id' => $goodsId
'goods_id' => $goodsId,
'page' => 1,
'size' => 100,
];
return [$type, $appendParams];
}
public static function incrQuantity($goodsId, $quantity, $skuId, $modifyType = 2)

View File

@ -2,7 +2,6 @@
namespace App\Services\Business\KuaiTuanTuan;
use App\Listeners\BindBusinessGoods;
use App\Models\BusinessGoodsSku;
use App\Models\GoodsSku;
use App\Services\Business\BusinessClient;
@ -14,9 +13,9 @@ class KuaiTuanTuan extends BusinessClient
// 如果请求的content-type是 application/x-www-form-urlencoded所有参数值也做urlencode编码
// 如果是multipart/form-data格式每个表单字段的参数值无需编码但每个表单字段的charset需要指定为utf-8
// 如果指定接口返回数据格式为JSON请指明header头content-type: application/json
protected $clientId = '';
protected $clientId = '8d7ca13bc27247b6a04e08404b51dfd8';
protected $clientSecret = '';
protected $clientSecret = '4478bc82dc1e1f68fe06c9f2bc683f1dcb3e6d83';
protected $publicParams = [
'type' => '',
@ -31,19 +30,21 @@ class KuaiTuanTuan extends BusinessClient
public function auth()
{
$accessToken = $this->getAccessTokenWithCode();
$accessToken['scope'] = json_encode($accessToken['scope'], 256);
$accessToken['pop_auth_token_create_response'] = json_encode($accessToken, 256);
$accessToken['status'] = 1;
$this->shop->update($accessToken);
return $this->shop;
}
public function downloadGoods($page = 1)
public function downloadGoodsListAndBind($page = 1)
{
[$type, $appendParams] = Goods::downloadGoods($this->shop->owner_name, $page);
$res = $this->formDataPostRequest($type, $appendParams);
[$type, $appendParams] = Goods::downloadGoods($this->shop->owner_id, $page);
$res = $this->doRequest($type, $appendParams);
$goods = $res['ktt_goods_query_list_response']['goods_list'];
$this->bindGoods($goods);
$pageNum = ceil($res['total'] / $appendParams['size']);
$pageNum = ceil($res['ktt_goods_query_list_response']['total'] / $appendParams['size']);
if ($pageNum > $page && 10 >= $page) {
$this->downloadGoods($page + 1);
}
@ -54,39 +55,36 @@ class KuaiTuanTuan extends BusinessClient
Goods::bindGoods($goods, $this->shop->id);
}
public function incrQuantity($skuId)
public function incrQuantity($businessGoodsSku, $num, $incremental, GoodsSku $goodsSku)
{
$goodsSku = GoodsSku::query()->find($skuId);
$business = BusinessGoodsSku::query()->where('shop_id', $this->shop->id)->where('self_sku_id', $skuId)->first(['goods_id', 'sku_id']);
[$type, $appendParams] = Goods::incrQuantity($business->goodsId, $goodsSku->stock, $business->sku_id);
$this->formDataPostRequest($type, $appendParams);
$log = new Log();
$log->module = 'plat';
$log->action = 'POST';
$log->target_type = 'kuaituantuan';
$log->target_id = 0;
$log->target_field = $type;
$log->user_id = 1;
$log->message = 'success';
$log->save();
[$type, $appendParams] = Goods::incrQuantity($businessGoodsSku->goods_id, $num, $businessGoodsSku->sku_id, $incremental ? 1 : 2);
$this->doRequest($type, $appendParams);
}
public function downloadOrders($beginTime, $endTime, $activityNo, $downloadType = 'default')
/**
* 下载没有发起售后,未取消的订单
*
* @param $beginTime
* @param $endTime
* @param int $page
* @param string $downloadType
* @return void
*/
public function downloadOrdersAndSave($beginTime, $endTime, $downloadType = 'default', $page = 1)
{
if ('increment' === $downloadType) {
[$type, $appendParams] = Order::downloadIncrementOrders($beginTime, $endTime, $activityNo);
[$type, $appendParams] = Order::downloadIncrementOrders($beginTime, $endTime, $page);
$responseName = 'ktt_increment_order_query_response';
} else {
[$type, $appendParams] = Order::downloadOrders($beginTime, $endTime, $activityNo);
[$type, $appendParams] = Order::downloadOrders($beginTime, $endTime, $page);
$responseName = 'ktt_order_list_response';
}
$res = $this->formDataPostRequest($type, $appendParams);
return $res['ktt_order_list_response']['order_list'];
$res = $this->doRequest($type, $appendParams);
$this->saveOrders($res[$responseName]['order_list']);
$pageNum = ceil($res[$responseName]['total_count'] / $appendParams['page_size']);
if ($pageNum > $page && 10 >= $page) {
$this->downloadOrdersAndSave($beginTime, $endTime, $page + 1);
}
public function saveOrders($orders)
{
Order::saveOrders($orders, $this->shop->id);
}
protected function getAccessTokenWithCode()
@ -99,7 +97,7 @@ class KuaiTuanTuan extends BusinessClient
protected function getSign($params)
{
$params = ksort($params);
ksort($params);
$str = '';
foreach ($params as $key => $val) {
$str .= $key . $val;
@ -109,7 +107,7 @@ class KuaiTuanTuan extends BusinessClient
return strtoupper(md5($str));
}
public function doRequest($type, $appendParams = [], $url = 'http://gw-api.pinduoduo.com/api/router')
public function doRequest($type, $appendParams = [], $url = 'https://gw-api.pinduoduo.com/api/router')
{
$publicParams = [
'type' => $type,
@ -117,7 +115,7 @@ class KuaiTuanTuan extends BusinessClient
'timestamp' => time()
];
if ('pdd.pop.auth.token.create' !== $type) {
$publicParams['access_token'] = $this->getAccessToken();
$publicParams['access_token'] = $this->getShop()->access_token;
}
$publicParams = array_merge($publicParams, $appendParams);
$publicParams['sign'] = $this->getSign($publicParams);
@ -125,6 +123,18 @@ class KuaiTuanTuan extends BusinessClient
return $this->formDataPostRequest($url, $publicParams);
}
public function downloadGoods($skuId)
{
$goodsSku = GoodsSku::query()
->with(['goods:id,goods_code'])
->find($skuId);
$code = $goodsSku->goods->goods_code . '_' . $goodsSku->sku_code;
$business = BusinessGoodsSku::query()->where('shop_id', $this->shop->id)->where('external_sku_id', $code)->first(['goods_id', 'sku_id']);
[$type, $appendParams] = Goods::downloadSingle($business->goods_id);
$res = $this->doRequest($type, $appendParams);
$goods = $res['response']['result'];
$this->bindGoods([$goods]);
}
public function getAuthUrl($shopId, $platId)
{

View File

@ -2,25 +2,23 @@
namespace App\Services\Business\KuaiTuanTuan;
use App\Models\BusinessOrder;
class Order
{
/**
* 根据成交时间拉取订单列表
*/
public static function downloadOrders($beginTime, $endTime, $activityNo)
public static function downloadOrders($beginTime, $endTime, $page = 1)
{
$type = 'pdd.ktt.order.list';
$appendParams = [
'confirm_at_begin' => $beginTime, // 成交启始时间, 必填,毫秒时间戳
'confirm_at_end' => $endTime, // 成交结束时间,必填, 毫秒时间戳,成交结束时间 - 成交启始时间 <= 24h
'page_number' => 1, // 页码, 必填
'page_number' => $page, // 页码, 必填
'page_size' => 100, // 数量, 必填, 1100
// 非必填
'activity_no' => $activityNo, // 团号
'after_sales_status' => 0, // 售后状态, 可选 0-未发起售后 1-退款中 2-退款成功 3-待处理 4-拒绝退款 6-待(顾客)退货 7-待(团长)确认退货 8-(顾客)撤销 9-(系统)关闭
'cancel_status' => 0, // 取消状态, 可选 0-未取消 1-已取消
// 'activity_no' => $activityNo, // 团号
// 'after_sales_status' => 0, // 售后状态, 可选 0-未发起售后 1-退款中 2-退款成功 3-待处理 4-拒绝退款 6-待(顾客)退货 7-待(团长)确认退货 8-(顾客)撤销 9-(系统)关闭
// 'cancel_status' => 0, // 取消状态, 可选 0-未取消 1-已取消
// 'shipping_status' => '', // 发货状态 0-未发货 1-已发货 2-部分发货 3-已收货
// 'verification_status' => '', // 核销状态, 可选 0-未核销 1-已核销 2-部分核销
];
@ -31,40 +29,23 @@ class Order
/**
* 快团团增量查订单
*/
public static function downloadIncrementOrders($beginTime, $endTime, $activityNo)
public static function downloadIncrementOrders($beginTime, $endTime, $page = 1)
{
$type = 'pdd.ktt.increment.order.query';
$appendParams = [
'start_updated_at' => $beginTime, // 更新起始时间
'end_updated_at' => $endTime, // 更新结束时间
'page_number' => 1, // 页码
'page_number' => $page, // 页码
'page_size' => 100, // 数量
// 非必填
'activity_no' => $activityNo, // 团号
'after_sales_status' => 0, // 售后状态, 可选 0-未发起售后 1-退款中 2-退款成功 3-待处理 4-拒绝退款 6-待(顾客)退货 7-待(团长)确认退货 8-(顾客)撤销 9-(系统)关闭
'cancel_status' => 0, // 取消状态, 可选 0-未取消 1-已取消
// 'activity_no' => $activityNo, // 团号
// 'after_sales_status' => 0, // 售后状态, 可选 0-未发起售后 1-退款中 2-退款成功 3-待处理 4-拒绝退款 6-待(顾客)退货 7-待(团长)确认退货 8-(顾客)撤销 9-(系统)关闭
// 'cancel_status' => 0, // 取消状态, 可选 0-未取消 1-已取消
// 'shipping_status' => '', // 发货状态 0-未发货 1-已发货 2-部分发货 3-已收货
// 'verification_status' => '', // 核销状态, 可选 0-未核销 1-已核销 2-部分核销
];
return [$type, $appendParams];
}
// 下载订单事件之后去统计 然后更新库存
public static function saveOrders(array $orders, $shopId)
{
foreach ($orders as $order) {
$orderRecord = BusinessOrder::updateOrCreate(
['shop_id' => $shopId, 'order_sn' => $order['order_sn']],
$order
);
foreach ($order['sub_order_list'] as $item) {
$orderRecord = BusinessOrder::updateOrCreate(
['shop_id' => $shopId, 'business_order_id' => $orderRecord->id, 'goods_id' => $item['goods_id'], 'sku_id' => $item['sku_id']],
$item
);
}
}
}
}

View File

@ -19,11 +19,14 @@ class Goods
public static function incrQuantity($shopId, $quantity, $businessGoods)
{
return [
'data' => [
'stock' => $quantity,
'business_sku_id' => $businessGoods['sku_id'],
'business_goods_id' => $businessGoods['goods_id'],
'erp_shop_id' => $shopId,
'erp_sku_id' => $businessGoods['external_sku_id']
'erp_sku_id' => $businessGoods['external_sku_id'],
],
'type' => '更新库存',
];
}
}

View File

@ -2,9 +2,7 @@
namespace App\Services\Business\MiaoXuan;
use App\Models\BusinessGoodsSku;
use App\Models\GoodsSku;
use App\Models\Log;
use App\Services\Business\BusinessClient;
class MiaoXuan extends BusinessClient
@ -15,7 +13,7 @@ class MiaoXuan extends BusinessClient
// TODO: Implement auth() method.
}
public function downloadGoods()
public function downloadGoodsListAndBind()
{
}
@ -24,30 +22,19 @@ class MiaoXuan extends BusinessClient
Goods::bindGoods($goods, $this->shop->id);
}
public function incrQuantity($skuId)
public function incrQuantity($businessGoodsSku, $num, $incremental, GoodsSku $goodsSku)
{
$goodsSku = GoodsSku::query()->find($skuId);
$business = BusinessGoodsSku::query()->where('shop_id', $this->shop->id)->where('self_sku_id', $skuId)->first(['goods_id', 'sku_id', 'external_sku_id'])->toArray();
$appendParams = Goods::incrQuantity($this->shop->id, $goodsSku->stock, $business);
$url = ''; // http://shop.chutang66.com/miaoxuan/stock
$appendParams = Goods::incrQuantity($this->shop->id, $goodsSku->stock, $businessGoodsSku);
$url = 'http://shop.chutang66.com/miaoxuan/stock';
$this->formDataPostRequest($url, $appendParams);
$log = new Log();
$log->module = 'plat';
$log->action = 'POST';
$log->target_type = 'miaoxuan';
$log->target_id = 0;
$log->target_field = '更新库存';
$log->user_id = 1;
$log->message = 'success';
$log->save();
}
public function downloadOrders($beginTime, $endTime, $activityNo, $downloadType = 'default')
public function downloadOrdersAndSave($beginTime, $endTime, $downloadType = 'default', $page = 1)
{
}
public function saveOrders($orders)
public function downloadGoods($skuId)
{
Order::saveOrders($orders, $this->shop->id);
// TODO: Implement downloadGoods() method.
}
}

View File

@ -2,25 +2,7 @@
namespace App\Services\Business\MiaoXuan;
use App\Models\BusinessOrder;
class Order
{
// 下载订单事件之后去统计 然后更新库存
public static function saveOrders(array $orders, $shopId)
{
foreach ($orders as $order) {
$orderRecord = BusinessOrder::updateOrCreate(
['shop_id' => $shopId, 'order_sn' => $order['order_sn']],
$order
);
foreach ($order['sub_order_list'] as $item) {
$orderRecord = BusinessOrder::updateOrCreate(
['shop_id' => $shopId, 'business_order_id' => $orderRecord->id, 'goods_id' => $item['goods_id'], 'sku_id' => $item['sku_id']],
$item
);
}
}
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Utils;
class DateTimeUtils
{
/**
* 今天7点到明天7点算作今天
*/
public static function getToday()
{
$time = time();
$inventoryTime = strtotime(date('Y-m-d 07:00:00'));
if ($time < $inventoryTime) {
$time = strtotime('-1 day');
}
return date('Y-m-d', $time);
}
public static function getMicroTime($dateTime = null)
{
$time = microtime(true);
if ($dateTime && is_string($dateTime)) {
$time = strtotime($dateTime);
}
if ($dateTime && is_int($dateTime)) {
$time = $dateTime;
}
$time *= 1000;
return ceil($time);
}
}

View File

@ -33,18 +33,4 @@ class FormatUtils
}
return $data;
}
/**
* 今天7点到明天7点算作今天
*/
public static function date()
{
$time = time();
$inventoryTime = strtotime(date('Y-m-d 07:00:00'));
if ($time < $inventoryTime) {
$time = strtotime('-1 day');
}
return date('Y-m-d', $time);
}
}

View File

@ -9,7 +9,9 @@
"license": "MIT",
"require": {
"php": "^7.2.5|^8.0",
"ext-json": "*",
"aliyuncs/oss-sdk-php": "^2.5",
"beyondcode/laravel-websockets": "^1.13",
"fideloper/proxy": "^4.4",
"intervention/image": "^2.7",
"laravel/framework": "^6.20.26",

2734
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -67,7 +67,7 @@ return [
|
*/
'timezone' => 'UTC',
'timezone' => 'PRC',
/*
|--------------------------------------------------------------------------

View File

@ -52,8 +52,8 @@ return [
'username' => env('DB_USERNAME', ''),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
@ -71,7 +71,7 @@ return [
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'charset' => 'utf8mb4',
'prefix' => '',
'prefix_indexes' => true,
'schema' => 'public',
@ -86,7 +86,7 @@ return [
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'charset' => 'utf8mb4',
'prefix' => '',
'prefix_indexes' => true,
],

View File

@ -13,6 +13,7 @@ class CreateUsersTable extends Migration
*/
public function up()
{
Schema::defaultStringLength(191);
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
@ -23,6 +24,8 @@ class CreateUsersTable extends Migration
$table->softDeletes();
$table->rememberToken();
$table->timestamps();
// 索引
$table->unique('name');
});
}

View File

@ -13,6 +13,7 @@ class CreateGoodsTypesTable extends Migration
*/
public function up()
{
Schema::defaultStringLength(191);
Schema::create('goods_types', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->nullable(false);

View File

@ -13,6 +13,7 @@ class CreateGoodsBrandsTable extends Migration
*/
public function up()
{
Schema::defaultStringLength(191);
Schema::create('goods_brands', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->nullable(false);

View File

@ -13,6 +13,7 @@ class CreateGoodsTable extends Migration
*/
public function up()
{
Schema::defaultStringLength(191);
Schema::create('goods', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title')->nullable(false);

View File

@ -13,6 +13,7 @@ class CreateGoodsSkusTable extends Migration
*/
public function up()
{
Schema::defaultStringLength(191);
Schema::create('goods_skus', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('goods_id')->nullable(false)->comment('商品id');
@ -28,6 +29,7 @@ class CreateGoodsSkusTable extends Migration
$table->unsignedInteger('reserve')->default(0)->comment('预留量');
$table->timestamps();
// 索引
$table->unique(['goods_id', 'sku_code']);
});
}

View File

@ -20,8 +20,9 @@ class CreateDailyStockRecordsTable extends Migration
$table->unsignedInteger('arrived_today_num')->default(0)->comment('今日到货');
$table->unsignedInteger('loss_num')->default(0)->comment('损耗');
$table->unsignedInteger('inventory')->default(0)->comment('库存盘点');
$table->unique('sku_id', 'day');
$table->timestamps();
// 索引
$table->unique(['sku_id', 'day']);
});
}

View File

@ -13,6 +13,7 @@ class CreateMenusTable extends Migration
*/
public function up()
{
Schema::defaultStringLength(191);
Schema::create('menus', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('code', 32)->nullable(false)->comment('菜单编码');

View File

@ -13,21 +13,22 @@ class CreateShopsTable extends Migration
*/
public function up()
{
Schema::defaultStringLength(191);
Schema::create('shops', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
$table->unsignedTinyInteger('plat_id')->comment('平台id');
$table->string('access_token')->nullable();
$table->unsignedMediumInteger('expires_at')->nullable()->comment('access_token过期时间点');
$table->unsignedBigInteger('expires_at')->nullable()->comment('access_token过期时间点');
$table->unsignedInteger('expires_in')->nullable()->comment('access_token过期时间段10表示10秒后过期');
$table->string('owner_id')->nullable()->comment('商家店铺id');
$table->string('owner_name')->nullable()->comment('商家账号名称');
$table->string('refresh_token')->nullable()->comment('refresh token可用来刷新access_token');
$table->unsignedMediumInteger('refresh_token_expires_at')->nullable()->comment('Refresh token过期时间点');
$table->unsignedBigInteger('refresh_token_expires_at')->nullable()->comment('Refresh token过期时间点');
$table->unsignedInteger('refresh_token_expires_in')->nullable()->comment('refresh_token过期时间段10表示10秒后过期');
$table->text('scope')->nullable()->comment('接口列表');
$table->text('pop_auth_token_create_response')->nullable()->comment('授权认证信息');
$table->string('status')->default(0)->comment('状态');
$table->unsignedTinyInteger('status')->default(0)->comment('状态');
$table->softDeletes();
$table->timestamps();
});

View File

@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBusinessGoodsTable extends Migration
class CreateBusinessGoodsSkusTable extends Migration
{
/**
* Run the migrations.
@ -19,27 +19,27 @@ class CreateBusinessGoodsTable extends Migration
$table->bigInteger('self_sku_id')->nullable();
$table->string('activity_no')->nullable();
$table->string('category_name')->nullable();
$table->mediumInteger('create_time')->nullable();
$table->bigInteger('create_time')->nullable();
$table->text('goods_desc')->nullable();
$table->string('goods_id')->nullable();
$table->text('goods_image_list')->nullable();
$table->string('goods_name')->nullable();
$table->integer('is_activity_delete')->nullable();
$table->integer('limit_buy')->nullable();
$table->mediumInteger('market_price')->nullable();
$table->mediumInteger('update_time')->nullable();
$table->bigInteger('market_price')->nullable();
$table->bigInteger('update_time')->nullable();
$table->string('external_sku_id')->nullable();
$table->mediumInteger('goods_purchase_price')->nullable();
$table->mediumInteger('price_in_fen')->nullable();
$table->mediumInteger('quantity')->nullable();
$table->bigInteger('goods_purchase_price')->nullable();
$table->bigInteger('price_in_fen')->nullable();
$table->bigInteger('quantity')->nullable();
$table->integer('quantity_type')->nullable();
$table->mediumInteger('reserve_quantity')->nullable();
$table->mediumInteger('sku_id')->nullable();
$table->mediumInteger('sold_quantity')->nullable();
$table->bigInteger('reserve_quantity')->nullable();
$table->bigInteger('sku_id')->nullable();
$table->bigInteger('sold_quantity')->nullable();
$table->text('spec_list')->nullable();
$table->string('spec_name')->nullable();
$table->string('thumb_url')->nullable();
$table->mediumInteger('total_quantity')->nullable();
$table->bigInteger('total_quantity')->nullable();
$table->timestamps();
});
}

View File

@ -16,24 +16,24 @@ class CreateBusinessOrdersTable extends Migration
Schema::create('business_orders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('shop_id');
$table->bigInteger('activity_no')->nullable();
$table->string('activity_no')->nullable();
$table->string('activity_title')->nullable();
$table->mediumInteger('after_sales_status')->nullable();
$table->bigInteger('after_sales_status')->nullable();
$table->string('business_note')->nullable();
$table->string('buyer_memo')->nullable();
$table->integer('cancel_status')->nullable();
$table->mediumInteger('confirm_at')->nullable();
$table->mediumInteger('discount_amount')->nullable();
$table->bigInteger('confirm_at')->nullable();
$table->bigInteger('discount_amount')->nullable();
$table->string('help_sell_nickname')->nullable();
$table->string('inner_transaction_id')->nullable();
$table->boolean('is_supplier')->nullable();
$table->integer('logistics_type')->nullable();
$table->integer('mall_activity_type')->nullable();
$table->string('nick_name')->nullable();
$table->mediumInteger('order_amount')->nullable();
$table->bigInteger('order_amount')->nullable();
$table->string('order_sn')->nullable();
$table->integer('participate_no')->nullable();
$table->mediumInteger('platform_discount_amount')->nullable();
$table->bigInteger('platform_discount_amount')->nullable();
$table->string('receiver_address_city')->nullable();
$table->string('receiver_address_detail')->nullable();
$table->string('receiver_address_district')->nullable();
@ -46,14 +46,13 @@ class CreateBusinessOrdersTable extends Migration
$table->string('self_pick_up_contact_mobile')->nullable();
$table->string('self_pick_up_contact_name')->nullable();
$table->string('self_pick_up_site_name')->nullable();
$table->mediumInteger('service_amount')->nullable();
$table->mediumInteger('shipping_amount')->nullable();
$table->bigInteger('service_amount')->nullable();
$table->bigInteger('shipping_amount')->nullable();
$table->integer('shipping_status')->nullable();
$table->string('supply_activity_no')->nullable();
$table->integer('supply_participate_no')->nullable();
$table->mediumInteger('theoretical_refund_amount')->nullable();
$table->bigInteger('theoretical_refund_amount')->nullable();
$table->string('transaction_id')->nullable();
$table->mediumInteger('updated_at')->nullable();
$table->integer('verification_status')->nullable();
$table->timestamps();
});

View File

@ -21,21 +21,21 @@ class CreateBusinessOrderItemsTable extends Migration
$table->integer('cancel_status')->nullable();
$table->string('category_name')->nullable();
$table->string('external_sku_id')->nullable();
$table->mediumInteger('goods_amount')->nullable();
$table->mediumInteger('goods_cost_price')->nullable();
$table->mediumInteger('goods_id')->nullable();
$table->bigInteger('goods_amount')->nullable();
$table->bigInteger('goods_cost_price')->nullable();
$table->bigInteger('goods_id')->nullable();
$table->string('goods_name')->nullable();
$table->integer('goods_number')->nullable();
$table->mediumInteger('goods_price')->nullable();
$table->mediumInteger('goods_purchase_price')->nullable();
$table->bigInteger('goods_price')->nullable();
$table->bigInteger('goods_purchase_price')->nullable();
$table->string('goods_specification')->nullable();
$table->mediumInteger('help_sell_amount')->nullable();
$table->bigInteger('help_sell_amount')->nullable();
$table->boolean('is_supplier')->nullable();
$table->integer('need_verification_number')->nullable();
$table->integer('shipping_status')->nullable();
$table->mediumInteger('sku_id')->nullable();
$table->bigInteger('sku_id')->nullable();
$table->string('sub_order_sn')->nullable();
$table->mediumInteger('theoretically_refund_amount')->nullable();
$table->bigInteger('theoretically_refund_amount')->nullable();
$table->string('thumb_url')->nullable();
$table->integer('verification_number')->nullable();
$table->timestamps();

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('jobs');
}
}

View File

@ -27,7 +27,7 @@ class MenusTableSeeder extends Seeder
$id = DB::table('menus')->insertGetId(['parent_id' => 0,'code' => 'SYSTEM_MANAGE', 'name' => '系统管理', 'seq' => 3]);
DB::table('menus')->insert([
['parent_id' => $id,'code' => 'ROLE_MANAGE', 'name' => '角色管理', 'seq' => 0],
['parent_id' => $id,'code' => 'PERMISSION_MANAGE', 'name' => '权限管理', 'seq' => 1],
// ['parent_id' => $id,'code' => 'PERMISSION_MANAGE', 'name' => '权限管理', 'seq' => 1],
]);
// 系统日志
DB::table('menus')->insertGetId(['parent_id' => 0,'code' => 'SYSTEM_LOG', 'name' => '系统日志', 'seq' => 4]);

View File

@ -37,7 +37,7 @@ return [
'name' => '规格查看',
'parent_id' => 2,
],
'goods_skus.udpate' => [
'goods_skus.update' => [
'id' => 25,
'name' => '规格更新',
'parent_id' => 2,
@ -52,6 +52,11 @@ return [
'name' => '字段更新',
'parent_id' => 2,
],
'goods_sku.orders_num' => [
'id' => 28,
'name' => '店铺订单',
'parent_id' => 2,
],
'GOODS_TYPE' => [
'id' => 3,
'name' => '商品种类',
@ -210,11 +215,11 @@ return [
'name' => '设置权限',
'parent_id' => 8,
],
'PERMISSION_MANAGE' => [
'id' => 9,
'name' => '权限管理',
'parent_id' => 7,
],
// 'PERMISSION_MANAGE' => [
// 'id' => 9,
// 'name' => '权限管理',
// 'parent_id' => 7,
// ],
'permissions.index' => [
'id' => 90,
'name' => '列表',

0
resources/templates/goods_skus_import.xlsx Normal file → Executable file
View File

View File

@ -33,6 +33,7 @@ Route::middleware(['auth:api', 'check.permissions'])->group(function () {
Route::patch('single/goods_skus/{id}', [GoodsSkusController::class, 'updateField'])->name('goods_sku.single_update');
// 店铺
Route::resource('shops', 'Shop\ShopsController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]);
Route::get('count/orders/num', [ShopsController::class, 'countOrdersNumWithSkuCode'])->name('goods_sku.orders_num');
// 角色
Route::resource('roles', 'Role\RolesController', ['only' => ['index', 'store', 'show', 'update']]);
Route::post('roles/{id}/permissions', [RolesController::class, 'addPermissions'])->name('roles.permission');