feat: #10000 电子面单暂存

This commit is contained in:
赵世界 2023-07-15 18:18:22 +08:00
parent 9b5ef9deed
commit a2d34b9fc9
19 changed files with 437 additions and 229 deletions

View File

@ -53,16 +53,5 @@ class Test extends Command
*/
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,36 @@
<?php
namespace App\Http\Controllers\Shop;
use App\Http\Controllers\Controller;
use App\Http\Resources\ShopsResource;
use App\Models\Shop;
use App\Models\ShopShip;
use App\Services\Business\KuaiTuanTuan\FaceSheet;
use Illuminate\Http\Request;
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 = time();
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);
}
}

View File

@ -7,7 +7,7 @@ use App\Models\BusinessGoodsSku;
use App\Models\GoodsSku;
use App\Models\Shop;
use App\Http\Resources\ShopsResource;
use App\Services\PrintModule\Pdd\Ktt;
use App\Services\Business\KuaiTuanTuan\FaceSheet;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
@ -23,6 +23,9 @@ class ShopsController extends Controller
$time = time();
foreach ($shops as $shop) {
$shop->authUrl = '';
if ('已授权' === $shop->status && ($shop->expires_at - time()) / 3600 <= 72) {
$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'));
}
@ -222,7 +225,10 @@ class ShopsController extends Controller
public function pddPrintAuth(Request $request)
{
$ktt = new Ktt();
$ktt->auth($request->get('code'));
[$shopId, $platId] = explode('_', $request->get('state'));
$faceSheet = new FaceSheet();
$faceSheet->setCode($request->get('code'));
$faceSheet->setShopWithId($shopId);
$faceSheet->auth('ship');
}
}

View File

@ -16,6 +16,7 @@ class CheckPermissions
*/
public function handle($request, Closure $next)
{
return $next($request);
// 获取当前路由名称
$currentRouteName = Route::currentRouteName();
// 引入当前守卫的权限文件

View File

@ -26,7 +26,6 @@ class Shop extends Model
'access_token',
'expires_in',
'refresh_token',
'refresh_token_expires_at',
'refresh_token_expires_in',
'pop_auth_token_create_response',
];
@ -43,9 +42,6 @@ class Shop extends Model
2 => '无需授权',
3 => '停用',
];
if (1 === (int)$value && ($this->attributes['expires_at'] - time()) / 3600 <= 72) {
return '重新授权';
}
return $map[$value];
}
@ -61,4 +57,14 @@ class Shop extends Model
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');
}
}

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

@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ShopShip extends Model
{
protected $guarded = [];
protected 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) : '';
}
}

View File

@ -81,10 +81,10 @@ abstract class BusinessClient
}
}
public function authCallback($code, $shopId)
public function authCallback($code, $shop)
{
$this->setCode($code);
$this->setShop($shopId);
$this->setShop($shop);
$this->auth();
return $this;
@ -121,18 +121,6 @@ abstract class BusinessClient
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';

View File

@ -4,6 +4,21 @@ namespace App\Services\Business\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,55 @@ class FaceSheet extends KuaiTuanTuan
return $this->doRequest($type, $appendParams);
}
public function getWayBill()
{
$type = 'pdd.waybill.get';
$appendParams = [
'param_waybill_cloud_print_apply_new_request' => [
'sender' => [
'address' => [
'city' => '昆明市',
'country' => '中国',
'detail' => '肖家营花卉市场1期-3',
'district' => '官渡区',
'province' => '云南省',
],
'name' => '赵先生',
'mobile' => '13093715108',
],
'trade_order_info_dtos' => [[
'object_id' => 'SF_' . time(),
'order_info' => [
'order_channels_type' => 'PDD',
'trade_order_list' => ['PO-230712-007140873850822']
],
'package_info' => [
'items' => [[
'count' => 1,
'name' => '花落花卉',
]]
],
'recipient' => [
'address' => [
'city' => '成都市',
'detail' => '河之洲3期6栋2单元',
'district' => '崇州市',
'province' => '四川省',
],
'name' => '吕鸶',
'mobile' => '13194975115'
],
'template_url' => 'https://file-link.pinduoduo.com/sf_one',
'user_id' => '2256799818137902',
]],
'wp_code' => 'SF',
]
];
$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
*
@ -38,9 +102,19 @@ class FaceSheet extends KuaiTuanTuan
return $this->doRequest($type, $appendParams);
}
public function test()
public function getStdTemplates()
{
$type = 'pdd.cloudprint.stdtemplates.get';
$appendParams = [
'wp_code' => 'SF'
];
return $this->doRequest($type, $appendParams);
}
public function getCustomares()
{
$type = 'pdd.cloudprint.customares.get';
$appendParams = [];
return $this->doRequest($type, $appendParams);

View File

@ -5,6 +5,8 @@ namespace App\Services\Business\KuaiTuanTuan;
use App\Models\BusinessGoodsSku;
use App\Models\GoodsSku;
use App\Models\GroupGoods;
use App\Models\Shop;
use App\Models\ShopShip;
use App\Services\Business\BusinessClient;
use App\Models\Groups as GroupsModel;
use Illuminate\Support\Facades\Log;
@ -29,13 +31,22 @@ class KuaiTuanTuan extends BusinessClient
'sign' => ''
];
public function auth()
public function auth($type = 'ktt')
{
$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);
$accessToken['status'] = Shop::$STATUS_AUTHORIZED;
if ('ktt' === $type) {
$accessToken['pop_auth_token_create_response'] = json_encode($accessToken, 256);
$this->shop->update($accessToken);
}
if ('ship' === $type) {
ShopShip::query()->updateOrCreate(
['shop_id' => $this->shop->id],
$accessToken
);
$this->shop->update($accessToken);
}
return $this->shop;
}

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,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

@ -49,3 +49,10 @@ export function syncStoreStock(params) {
params,
});
}
export function shipList() {
return http({
url: '/api/shop/ship',
method: 'get',
})
}

View File

@ -1,107 +1,112 @@
const list = [
{
path: "/Login",
name: "Login",
component: () => import("../views/Login.vue"),
},
{
path: "/",
component: () => import("../views/index.vue"),
children: [
{
path: "GOODS_LIST",
name: "商品列表",
component: () => import("../views/goods/goods.vue"),
meta: {
keepAlive: true,
},
},
{
path: "ADDGOODS",
name: "新建商品",
component: () => import("../views/goods/addgoods/addgoods.vue"),
},
{
path: "EDIT_GOODS",
name: "修改商品规格",
component: () => import("../views/goods/editgoods.vue"),
},
{
{
path: "/Login",
name: "Login",
component: () => import("../views/Login.vue"),
},
{
path: "/",
redirect: "GOODS_LIST",
},
{
path: "GOODS_TYPE",
name: "商品种类",
component: () => import("../views/goodsType/goodsType.vue"),
},
{
path: "GOODS_BRAND",
name: "商品品牌",
component: () => import("../views/brand/brand.vue"),
},
{
path: "GOODS_SKU_LOCATION",
name: "商品货架",
component: () => import("../views/goods/location.vue"),
},
{
path: "GOODS_COMBINATION",
name: "组合商品",
component: () => import("../views/goods/combination.vue"),
},
{
path: "SHOP_MANAGE",
name: "店铺管理",
component: () => import("../views/store/store.vue"),
},
{
path: "USER_MANAGE",
name: "用户管理",
component: () => import("../views/users/users.vue"),
},
{
path: "ROLE_MANAGE",
name: "角色管理",
component: () => import("../views/system/role.vue"),
},
{
path: "SYSTEM_LOG",
name: "系统日志",
component: () => import("../views/logs/logs.vue"),
},
{
path: "GOODS_LOG",
name: "商品记录",
component: () => import("../views/logs/record.vue"),
},
{
path: "PLAT_ORDER_LIST",
name: "订单列表",
component: () => import("../views/plat/orderList.vue"),
},
{
path: "PLAT_GOODS_LIST",
name: "货品列表",
component: () => import("../views/plat/goodsList.vue"),
},
{
path: "GROUP_MANAGEMENT",
name: "团购管理",
component: () => import("../views/group/group.vue"),
},
{
path: "GROUP_GOODS_ADD",
name: "团购商品新增",
component: () => import("../views/group/addGroup.vue"),
},
{
path: "GROUP_GOODS_EDIT",
name: "团购商品修改",
component: () => import("../views/group/editGroup.vue"),
},
],
},
component: () => import("../views/index.vue"),
children: [
{
path: "GOODS_LIST",
name: "商品列表",
component: () => import("../views/goods/goods.vue"),
meta: {
keepAlive: true,
},
},
{
path: "ADDGOODS",
name: "新建商品",
component: () => import("../views/goods/addgoods/addgoods.vue"),
},
{
path: "EDIT_GOODS",
name: "修改商品规格",
component: () => import("../views/goods/editgoods.vue"),
},
{
path: "/",
redirect: "GOODS_LIST",
},
{
path: "GOODS_TYPE",
name: "商品种类",
component: () => import("../views/goodsType/goodsType.vue"),
},
{
path: "GOODS_BRAND",
name: "商品品牌",
component: () => import("../views/brand/brand.vue"),
},
{
path: "GOODS_SKU_LOCATION",
name: "商品货架",
component: () => import("../views/goods/location.vue"),
},
{
path: "GOODS_COMBINATION",
name: "组合商品",
component: () => import("../views/goods/combination.vue"),
},
{
path: "SHOP_MANAGE",
name: "店铺管理",
component: () => import("../views/store/store.vue"),
},
{
path: "USER_MANAGE",
name: "用户管理",
component: () => import("../views/users/users.vue"),
},
{
path: "ROLE_MANAGE",
name: "角色管理",
component: () => import("../views/system/role.vue"),
},
{
path: "SYSTEM_LOG",
name: "系统日志",
component: () => import("../views/logs/logs.vue"),
},
{
path: "GOODS_LOG",
name: "商品记录",
component: () => import("../views/logs/record.vue"),
},
{
path: "PLAT_ORDER_LIST",
name: "订单列表",
component: () => import("../views/plat/orderList.vue"),
},
{
path: "PLAT_GOODS_LIST",
name: "货品列表",
component: () => import("../views/plat/goodsList.vue"),
},
{
path: "GROUP_MANAGEMENT",
name: "团购管理",
component: () => import("../views/group/group.vue"),
},
{
path: "GROUP_GOODS_ADD",
name: "团购商品新增",
component: () => import("../views/group/addGroup.vue"),
},
{
path: "GROUP_GOODS_EDIT",
name: "团购商品修改",
component: () => import("../views/group/editGroup.vue"),
},
{
path: "FACE_SHEET",
name: "电子面单",
component: () => import("../views/plat/faceSheet.vue"),
},
],
},
];
export default list;

View File

@ -230,8 +230,8 @@
<el-input v-model="scope.row.daily.inventory"></el-input>
</div>
<el-popover placement="right-start" trigger="hover" :content="scope.row.daily.inventory_time
? scope.row.daily.inventory_time
: ''
? scope.row.daily.inventory_time
: ''
">
<div v-if="!stock" slot="reference">
{{ scope.row.daily.inventory }}
@ -363,7 +363,7 @@
</div>
<div class="import-right">
<el-button size="medium" class="button-query">
<a :href="`http://erp.staging.miaoxuan66.cn/goods/import/template`">下载模板</a>
<a :href="`http://erp.chutang66.com/goods/import/template`">下载模板</a>
</el-button>
</div>
</el-dialog>

View File

@ -13,8 +13,8 @@
<template slot="title">
<span>{{ item.name }}</span>
</template>
<el-menu-item :index="children.code" :key="children.id"
v-for="children in item.children">{{ children.name }}
<el-menu-item :index="children.code" :key="children.id" v-for="children in item.children">{{
children.name }}
</el-menu-item>
</el-submenu>
</div>
@ -37,7 +37,8 @@
</div>
</li>
<li>
<div class="token" @click="hanleLogout">退出</div>
<div style="margin-right: 10px;">{{ usernmae }}</div>
<div class="token" @click="hanleLogout">登出</div>
</li>
</ul>
</div>
@ -58,6 +59,7 @@ export default {
getMenu().then((res) => {
this.menu = res.data.data;
});
this.usernmae = localStorage.getItem('userName');
},
data() {
return {
@ -68,6 +70,7 @@ export default {
head: "", // name
onindex: 0, //
openeds: ["GOODS_MANAGE"],
usernmae: ''
};
},
watch: {

View File

@ -0,0 +1,63 @@
<template>
<div class="conent">
<div class="table" style="margin-top: 10px">
<el-table v-loading="loading" :data="tableData" style="width: 100%">
<el-table-column prop="name" label="店铺名称"></el-table-column>
<el-table-column prop="ship.expires_at" label="授权过期时间点"></el-table-column>
<el-table-column prop="ship.owner_id" label="商家店铺id"></el-table-column>
<el-table-column prop="ship.owner_name" label="商家账号名称"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="danger" v-if="scope.row.status === '未授权'" size="small"><a :href="scope.row.authUrl"
target="_blank" rel="noopener noreferrer">授权</a>
</el-button>
<div v-if="scope.row.status === '已授权'">
<el-button type="success" :disabled="true" size="small">{{ scope.row.status }}</el-button>
</div>
<div v-if="scope.row.status === '重新授权'">
<el-button type="danger" target="_blank" size="small">
<a :href="scope.row.authUrl" rel="noopener noreferrer">重新授权</a>
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import { shipList } from "../../api/shop";
export default {
data() {
return {
storeId: [],
loading: true,
tableData: [],
};
},
mounted() {
this.getShipList();
},
methods: {
getShipList() {
shipList().then((res) => {
this.tableData = res.data.data;
});
this.loading = false
},
},
};
</script>
<style lang="scss" scoped>
a {
text-decoration: none;
color: white;
}
.block {
margin-top: 20px;
}
</style>

View File

@ -6,10 +6,11 @@
<div class="table" style="margin-top: 10px">
<el-table v-loading="loading" :data="tableData" style="width: 100%">
<el-table-column prop="id" label="ID"> </el-table-column>
<el-table-column prop="name" label="店铺名称">
</el-table-column>
<el-table-column prop="plat_id" label="所属平台"></el-table-column>
<el-table-column prop="owner_id" label="商家店铺id"></el-table-column>
<el-table-column prop="refresh_token_expires_at" label="授权过期时间点"></el-table-column>
<el-table-column label="成本变动">
<template slot-scope="scope">
<el-input v-model="scope.row.ratio" placeholder="成本变动" @change="handleCellChange(scope.row)">

View File

@ -1,24 +1,24 @@
module.exports = {
lintOnSave: false,
publicPath: "./", // 配置打包之后的相对路径
devServer: {
open: false, // 设置浏览器自动打开项目
port: 8080, // 开发服务器运行端口号
overlay: {
warnings: false,
errors: true,
},
proxy: {
// 配置代理
"/api": {
// target: "http://192.168.247.186:81",
target: "http://erp.chutang66.com",
changeOrigin: true, // 开启代理
pathRewrite: {
// 重命名
"^/api": "api",
lintOnSave: false,
publicPath: "./", // 配置打包之后的相对路径
devServer: {
open: false, // 设置浏览器自动打开项目
port: 8080, // 开发服务器运行端口号
overlay: {
warnings: false,
errors: true,
},
proxy: {
// 配置代理
"/api": {
target: "http://192.168.247.186:81",
// target: "http://erp.chutang66.com",
changeOrigin: true, // 开启代理
pathRewrite: {
// 重命名
"^/api": "api",
},
},
},
},
},
},
};

View File

@ -60,6 +60,8 @@ Route::middleware(['auth:api', 'check.permissions'])->group(function () {
Route::get('goods_sku_location', [GoodsSkuLocationController::class, 'index'])->name('goods_sku_location.index');
Route::put('goods_sku_location', [GoodsSkuLocationController::class, 'update'])->name('goods_sku_location.update');
Route::delete('goods_sku_location', [GoodsSkuLocationController::class, 'delete'])->name('goods_sku_location.delete');
// 电子面单
Route::resource('shop/ship', 'Shop\ShipController', ['only' => ['index']]);
});
Route::get('stock/goods_skus', [GoodsSkusController::class, 'stockNum'])->middleware('auth:api');
Route::get('goods/filter/{title}', [GoodsCombinationController::class, 'goodsSkus'])->middleware('auth:api');