feat(haichao): 新增聚水潭ERP集成功能
- 新增聚水潭回调接口及事件处理机制 - 添加聚水潭商品、订单、售后同步任务 - 实现聚水潭订单推送及售后状态同步 - 新增手动同步聚水潭商品功能 - 在各路由组添加聚水潭中间件支持
This commit is contained in:
@@ -22,10 +22,12 @@ use app\services\product\shipping\ShippingTemplatesServices;
|
||||
use app\services\product\sku\StoreProductAttrServices;
|
||||
use app\services\product\sku\StoreProductAttrValueServices;
|
||||
use app\services\product\sku\StoreProductRuleServices;
|
||||
use app\jobs\product\ProductSyncErp;
|
||||
use crmeb\services\FileService;
|
||||
use crmeb\services\SpreadsheetExcelService;
|
||||
use crmeb\services\UploadService;
|
||||
use think\annotation\Inject;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* Class StoreProduct
|
||||
@@ -992,6 +994,39 @@ class StoreProduct extends AuthController
|
||||
return $this->success('已加入消息队列进行同步');
|
||||
}
|
||||
|
||||
public function sync_jst_product()
|
||||
{
|
||||
[$spuStr, $sync] = $this->request->postMore([
|
||||
['spu_str', ""],
|
||||
['sync', 0]
|
||||
], true);
|
||||
if (!$spuStr) {
|
||||
return $this->fail('请输入ERP商品SPU');
|
||||
}
|
||||
$spus = array_unique(array_filter(explode(',', $spuStr)));
|
||||
$traceId = uniqid('jst_', true);
|
||||
Log::info('admin.manual.jst.product.sync.dispatch', [
|
||||
'trace_id' => $traceId,
|
||||
'admin_id' => $this->adminId,
|
||||
'admin_type' => $this->adminType,
|
||||
'supplier_id' => $this->supplierId,
|
||||
'client_ip' => $this->request->ip(),
|
||||
'count' => count($spus),
|
||||
'spus' => $spus
|
||||
]);
|
||||
if ($sync) {
|
||||
foreach ($spus as $spu) {
|
||||
(new ProductSyncErp())->productFromErp($spu, (int)$this->supplierId);
|
||||
}
|
||||
return $this->success('同步完成');
|
||||
} else {
|
||||
foreach ($spus as $spu) {
|
||||
ProductSyncErp::dispatchDo('productFromErp', [$spu, (int)$this->supplierId]);
|
||||
}
|
||||
return $this->success('正在同步中,请稍后查看');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品批量操作
|
||||
* @param StoreProductBatchProcessServices $batchProcessServices
|
||||
|
||||
@@ -39,6 +39,8 @@ use Joypack\Tencent\Map\Bundle\LocationOption;
|
||||
use app\Request;
|
||||
use crmeb\services\CacheService;
|
||||
use crmeb\services\UploadService;
|
||||
use think\facade\Log;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* 公共类
|
||||
@@ -131,6 +133,22 @@ class PublicController
|
||||
return app('json')->success($data);
|
||||
}
|
||||
|
||||
public function jstCallback(Request $request)
|
||||
{
|
||||
$data = json_decode((string)$request->getContent(), true);
|
||||
if (!is_array($data)) {
|
||||
$data = $request->param();
|
||||
}
|
||||
$actionCode = $data['action_code'] ?? ($request->param('action_code') ?: '');
|
||||
Log::info('jst.api.callback ' . $actionCode);
|
||||
if ($actionCode === 'business_sku_syn') (new \app\haichao\callback\event\JstProductEvent())->event($request);
|
||||
if ($actionCode === 'sync_shipped') (new \app\haichao\callback\event\JstOrderPostEvent())->event($request);
|
||||
if ($actionCode === 'orderPost') (new \app\haichao\callback\event\JstOrderPostEvent())->event($request);
|
||||
if ($actionCode === 'orderRefund') (new \app\haichao\callback\event\JstOrderRefundEvent())->event($request);
|
||||
if ($actionCode === 'trade_after_sale') (new \app\haichao\callback\event\JstTradeEvent())->event($request);
|
||||
return Response::create(['code' => 0, 'msg' => 'ok'], 'json');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取个人中心菜单
|
||||
* @param Request $request
|
||||
|
||||
@@ -38,7 +38,9 @@ return [
|
||||
|
||||
//定时执行
|
||||
'crontab' => (file_exists(root_path() . "public/install/") && !file_exists(root_path() . "public/install/install.lock")) || !env('TIMER_ENABLE', false) ? [] : [
|
||||
\app\listener\system\timer\SystemTimer::class,//定时任务
|
||||
\app\listener\system\timer\SystemTimer::class,
|
||||
\app\haichao\task\JstSyncTask::class,
|
||||
\app\haichao\task\JstSyncStorkTask::class,
|
||||
],
|
||||
|
||||
'user.register' => [\app\listener\user\Register::class], //用户注册事件
|
||||
|
||||
86
app/haichao/HaichaoAop.php
Normal file
86
app/haichao/HaichaoAop.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao;
|
||||
|
||||
use app\http\middleware\InstallMiddleware;
|
||||
use app\Request;
|
||||
use app\services\order\StoreOrderServices;
|
||||
use think\Response;
|
||||
use think\route\RuleItem;
|
||||
use app\haichao\callback\JstCallbackEventManager;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
|
||||
class HaichaoAop extends InstallMiddleware
|
||||
{
|
||||
public function handle(Request $request, \Closure $next)
|
||||
{
|
||||
$haichaoJstServices = HaichaoJstServices::getServices();
|
||||
/** @var RuleItem $rule */
|
||||
$rule = $request->rule();
|
||||
Log::info('jst.aop.route ' . $rule->getName());
|
||||
switch ($rule->getName()) {
|
||||
case 'StoreOrderSplitDelivery':
|
||||
case 'StoreOrderUpdateDelivery':
|
||||
{
|
||||
$postInfo = $request->getMore(['delivery_name', 'delivery_id']);
|
||||
$param = $request->param();
|
||||
Log::info('jst.aop.delivery ' . json_encode([$postInfo, $param], JSON_UNESCAPED_UNICODE));
|
||||
$haichaoJstServices->updateStoreOrderDelivery($postInfo['delivery_id'] ?? '', $postInfo['delivery_name'] ?? '', $param['id'] ?? 0);
|
||||
$rt = $next($request);
|
||||
break;
|
||||
}
|
||||
case 'StoreOrderApplRefund':
|
||||
{
|
||||
$orderId = $request->param(['id'])['id'] ?? 0;
|
||||
Log::info('jst.aop.apply_refund ' . $orderId);
|
||||
$rt = $next($request);
|
||||
break;
|
||||
}
|
||||
case 'StoreOrderUpdateRefund':
|
||||
{
|
||||
$param = $request->param();
|
||||
$orderId = $param['id'] ?? 0;
|
||||
Log::info('jst.aop.update_refund ' . $orderId);
|
||||
$rt = $next($request);
|
||||
break;
|
||||
}
|
||||
case 'StoreOrderRefundRemark':
|
||||
{
|
||||
$orderId = $request->param(['id'])['id'] ?? 0;
|
||||
Log::info('jst.aop.refund_remark ' . $orderId);
|
||||
$rt = $next($request);
|
||||
break;
|
||||
}
|
||||
case 'StoreOrderorRemark':
|
||||
{
|
||||
$orderId = $request->param(['id'])['id'] ?? 0;
|
||||
Log::info('jst.aop.remark ' . $orderId);
|
||||
$rt = $next($request);
|
||||
break;
|
||||
}
|
||||
case 'orderPay':
|
||||
{
|
||||
$rt = $next($request);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
$rt = $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
// $actionCode = $request->getMore(['action_code'])['action_code'] ?? '';
|
||||
// if ($actionCode) {
|
||||
// try {
|
||||
// Log::info('jst.aop.callback ' . $actionCode);
|
||||
// return JstCallbackEventManager::callback($request);
|
||||
// } catch (\Throwable $e) {
|
||||
// Log::error('jst.aop.callback.err ' . $e->getMessage());
|
||||
// return Response::create(['msg' => 'callback error ' . $e->getMessage()], 'json');
|
||||
// }
|
||||
// }
|
||||
|
||||
return $rt;
|
||||
}
|
||||
}
|
||||
88
app/haichao/HaichaoBridge.php
Normal file
88
app/haichao/HaichaoBridge.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao;
|
||||
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
|
||||
abstract class HaichaoBridge
|
||||
{
|
||||
private array $api = [
|
||||
'appid' => '',
|
||||
'appSecret' => '',
|
||||
'api' => 'https://openapi.jushuitan.com/',
|
||||
'access_token' => '',
|
||||
'shopId' => '',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->api['appid'] = env('JST_APP_KEY', '');
|
||||
$this->api['appSecret'] = env('JST_APP_SECRET', '');
|
||||
$this->api['api'] = env('JST_API', 'https://openapi.jushuitan.com/');
|
||||
$this->api['access_token'] = env('JST_ACCESS_TOKEN', '');
|
||||
$this->api['shopId'] = (string)env('JST_SHOP_ID', '');
|
||||
}
|
||||
|
||||
public function getShopId(): string
|
||||
{
|
||||
return $this->api['shopId'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚水潭开放平台请求封装
|
||||
* @param string $api
|
||||
* @param array $data
|
||||
* @param array $addonBody
|
||||
* @return array
|
||||
*/
|
||||
protected function post(string $api, array $data = [], array $addonBody = []): array
|
||||
{
|
||||
if (!$this->api['appid'] || !$this->api['appSecret'] || !$this->api['access_token']) {
|
||||
throw new ValidateException('JST config missing');
|
||||
}
|
||||
$time = time();
|
||||
$baseBody = [
|
||||
'access_token' => $this->api['access_token'],
|
||||
'app_key' => $this->api['appid'],
|
||||
];
|
||||
$baseBody = array_merge($baseBody, $addonBody);
|
||||
$baseBody['biz'] = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
$baseBody['charset'] = 'utf-8';
|
||||
$baseBody['timestamp'] = $time;
|
||||
$baseBody['version'] = '2';
|
||||
$baseBody['sign'] = $this->sign($baseBody);
|
||||
$options['http'] = [
|
||||
'timeout' => 60,
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-Type: application/json;charset=utf-8',
|
||||
'content' => http_build_query($baseBody)
|
||||
];
|
||||
$rt = json_decode(file_get_contents("{$this->api['api']}$api", false, stream_context_create($options)), true);
|
||||
if (!is_array($rt) || !array_key_exists('code', $rt)) {
|
||||
Log::error('JST response invalid ' . json_encode([
|
||||
'api' => $api,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException('JST response invalid');
|
||||
}
|
||||
if ($rt['code'] != 0) {
|
||||
Log::error('JST request failed ' . json_encode([
|
||||
'api' => $api,
|
||||
'code' => $rt['code'],
|
||||
'msg' => (string)($rt['msg'] ?? ''),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException($rt['msg'] ?? 'JST error');
|
||||
}
|
||||
return $rt;
|
||||
}
|
||||
|
||||
private function sign(array $data): string
|
||||
{
|
||||
$computerStr = "";
|
||||
foreach ($data as $k => $v) {
|
||||
$computerStr .= $k . $v;
|
||||
}
|
||||
$computerStr = $this->api['appSecret'] . $computerStr;
|
||||
return md5($computerStr);
|
||||
}
|
||||
}
|
||||
815
app/haichao/HaichaoJstServices.php
Normal file
815
app/haichao/HaichaoJstServices.php
Normal file
@@ -0,0 +1,815 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao;
|
||||
|
||||
use app\services\order\StoreOrderCartInfoServices;
|
||||
use app\services\order\StoreOrderRefundServices;
|
||||
use app\services\order\StoreOrderServices;
|
||||
use app\services\supplier\SystemSupplierServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
|
||||
class HaichaoJstServices extends HaichaoBridge
|
||||
{
|
||||
/**
|
||||
* 同步物流公司信息并推送订单发货节点
|
||||
* @param string $postId
|
||||
* @param string $postName
|
||||
* @param int $oid
|
||||
* @return array
|
||||
*/
|
||||
public function updateStoreOrderDelivery(string $postId, string $postName, $oid = 0): array
|
||||
{
|
||||
$postList = [];
|
||||
$postList[] = [
|
||||
'l_id' => $postId,
|
||||
'l_name' => $postName,
|
||||
'warehouseRemark' => ''
|
||||
];
|
||||
$rt = $this->post('open/express/register/upload', $postList);
|
||||
$this->orderPush($oid, 'WAIT_BUYER_CONFIRM_GOODS', '', [], [
|
||||
'l_id' => $postId,
|
||||
'l_name' => $postName,
|
||||
'warehouseRemark' => ''
|
||||
]);
|
||||
return $rt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送订单到聚水潭(含支付/发货/退款等状态同步)
|
||||
* @param int $oid
|
||||
* @param string $status
|
||||
* @param string $rftype
|
||||
* @param array $rfinfo
|
||||
* @param array $extpm
|
||||
* @return bool
|
||||
*/
|
||||
public function orderPush(int $oid, string $status = 'WAIT_SELLER_SEND_GOODS', $rftype = '', $rfinfo = [], $extpm = []): bool
|
||||
{
|
||||
Log::info('jst.order_push.start ' . $oid . ' ' . $status);
|
||||
/** @var StoreOrderServices $orderServices */
|
||||
$orderServices = app()->make(StoreOrderServices::class);
|
||||
$order = $orderServices->get($oid, ['*'], ['store']);
|
||||
if (!$order || $order['id'] < 1) {
|
||||
throw new AdminException('订单信息不能为空');
|
||||
}
|
||||
if ($order['product_type'] != 4) {
|
||||
if ($order['product_type'] > 0 || !$order['paid']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$rid = $order['supplier_id'] ?? 0;
|
||||
$shopid = (int)$this->getShopId();
|
||||
if ($rid) {
|
||||
/** @var SystemSupplierServices $systemSupplierServices */
|
||||
$systemSupplierServices = app()->make(SystemSupplierServices::class);
|
||||
$info = $systemSupplierServices->getSupplierInfo((int)$rid, 'erp_shopid');
|
||||
if (!$info || !$info['erp_shopid']) return true;
|
||||
$shopid = (int)$info['erp_shopid'];
|
||||
if (!$shopid) return true;
|
||||
}
|
||||
$items = $this->getItems($oid, (int)$order['uid'], $order['unique'], $rftype);
|
||||
if (isset($extpm['l_id'])) {
|
||||
$order['delivery_id'] = $extpm['l_id'];
|
||||
$order['delivery_name'] = $extpm['l_name'];
|
||||
}
|
||||
$biz = [
|
||||
'shop_id' => $shopid,
|
||||
'so_id' => $order['order_id'],
|
||||
'shop_status' => $status,
|
||||
'buyer_message' => $order['mark'],
|
||||
'shop_buyer_id' => (string)$order['uid'],
|
||||
'receiver_name' => $order['real_name'],
|
||||
'receiver_mobile' => $order['user_phone'],
|
||||
'pay_amount' => (float)$order['pay_price'],
|
||||
'freight' => (float)$order['freight_price'],
|
||||
'order_date' => date('Y-m-d H:i:s', $order['add_time']),
|
||||
'shop_modified' => date('Y-m-d H:i:s', $order['add_time']),
|
||||
'items' => $items,
|
||||
'l_id' => $order['delivery_id'] ?? '',
|
||||
'logistics_company' => $order['delivery_name'] ?? '',
|
||||
'pay' => [],
|
||||
];
|
||||
if ($rfinfo) {
|
||||
$biz['pay'] = [
|
||||
'outer_pay_id' => $rfinfo['order_id'] ?? $order['order_id'],
|
||||
'pay_date' => date('Y-m-d H:i:s'),
|
||||
'payment' => $order['pay_type'],
|
||||
'seller_account' => 'seller',
|
||||
'buyer_account' => 'buyer' . $order['uid'],
|
||||
'amount' => 0 - (float)($rfinfo['refunded_price'] ?? 0)
|
||||
];
|
||||
$q = [
|
||||
'shop_id' => $shopid,
|
||||
'so_ids' => [$order['order_id']]
|
||||
];
|
||||
$resultb = $this->post('open/orders/single/query', $q)['data'];
|
||||
$biz['shop_status'] = $resultb['orders'][0]['shop_status'] ?? $status;
|
||||
} else {
|
||||
$biz['pay'] = [
|
||||
'outer_pay_id' => $order['order_id'],
|
||||
'pay_date' => date('Y-m-d H:i:s', $order['pay_time']),
|
||||
'payment' => $order['pay_type'],
|
||||
'seller_account' => 'seller',
|
||||
'buyer_account' => 'buyer' . $order['uid'],
|
||||
'amount' => (float)$order['pay_price']
|
||||
];
|
||||
}
|
||||
$addr = $this->getAddress($order['user_address']);
|
||||
$biz = array_merge($biz, $addr);
|
||||
if (isset($biz['l_id']) && $biz['l_id'] === '') {
|
||||
unset($biz['l_id']);
|
||||
}
|
||||
if (isset($biz['logistics_company']) && $biz['logistics_company'] === '') {
|
||||
unset($biz['logistics_company']);
|
||||
}
|
||||
if (isset($biz['receiver_zip']) && $biz['receiver_zip'] === '') {
|
||||
unset($biz['receiver_zip']);
|
||||
}
|
||||
if (isset($biz['items']) && is_array($biz['items'])) {
|
||||
foreach ($biz['items'] as $k => $item) {
|
||||
if (is_array($item) && isset($item['pic']) && $item['pic'] === '') {
|
||||
unset($item['pic']);
|
||||
$biz['items'][$k] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
$num = 3;
|
||||
for ($i = $num; $i >= 0; $i--) {
|
||||
if ($i <= 0) {
|
||||
Log::error('ERP订单上传失败 ' . json_encode([
|
||||
'api' => 'open/jushuitan/orders/upload',
|
||||
'shop_id' => $shopid,
|
||||
'so_id' => (string)($biz['so_id'] ?? ''),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$resp = $this->post('open/jushuitan/orders/upload', [$biz]);
|
||||
$result = $resp['data'] ?? [];
|
||||
if (!empty($result['datas'][0]['issuccess']) && $order['erp_order_id'] != $biz['so_id']) {
|
||||
$order->save(['erp_id' => $result['datas'][0]['o_id'], 'erp_order_id' => $biz['so_id']]);
|
||||
}
|
||||
break;
|
||||
} catch (\Throwable $e) {
|
||||
usleep(1000 * 50);
|
||||
Log::error('ERP订单上传失败:' . $e->getMessage() . ' ' . json_encode([
|
||||
'try' => ($num - $i + 1),
|
||||
'api' => 'open/jushuitan/orders/upload',
|
||||
'shop_id' => $shopid,
|
||||
'so_id' => (string)($biz['so_id'] ?? ''),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 售后单创建推送(本地 -> 聚水潭)
|
||||
* @param int $refundId
|
||||
* @return bool
|
||||
*/
|
||||
public function afterSaleCreatePush(int $refundId): bool
|
||||
{
|
||||
$api = 'open/aftersale/upload';
|
||||
Log::info('jst.after_sale.create.start ' . json_encode(['refund_id' => $refundId], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
/** @var StoreOrderRefundServices $storeOrderRefundServices */
|
||||
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
|
||||
$refund = $storeOrderRefundServices->get($refundId);
|
||||
if (!$refund) {
|
||||
Log::warning('jst.after_sale.create.skip ' . json_encode(['refund_id' => $refundId, 'reason' => 'refund_missing'], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
if (method_exists($refund, 'toArray')) {
|
||||
$refund = $refund->toArray();
|
||||
}
|
||||
|
||||
/** @var StoreOrderServices $storeOrderServices */
|
||||
$storeOrderServices = app()->make(StoreOrderServices::class);
|
||||
$order = $storeOrderServices->get((int)($refund['store_order_id'] ?? 0), ['*'], ['store']);
|
||||
if (!$order) {
|
||||
Log::warning('jst.after_sale.create.skip ' . json_encode(['refund_id' => $refundId, 'reason' => 'order_missing'], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (($order['product_type'] ?? 0) != 4) {
|
||||
if (($order['product_type'] ?? 0) > 0 || !($order['paid'] ?? 0)) {
|
||||
Log::info('jst.after_sale.create.skip ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'reason' => 'product_filter',
|
||||
'product_type' => $order['product_type'] ?? null,
|
||||
'paid' => $order['paid'] ?? null,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$shopid = $this->resolveShopIdByOrder($order);
|
||||
if (!$shopid) {
|
||||
Log::info('jst.after_sale.create.skip ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'reason' => 'missing_shopid',
|
||||
'supplier_id' => $order['supplier_id'] ?? 0
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
|
||||
$outerAsId = (string)($refund['order_id'] ?? '');
|
||||
if ($outerAsId === '') {
|
||||
$outerAsId = (string)$refundId;
|
||||
}
|
||||
|
||||
$items = $this->buildAfterSaleItemsFromRefund($refund);
|
||||
|
||||
$applyType = (int)($refund['apply_type'] ?? $refund['refund_type'] ?? 0);
|
||||
if ($applyType == 1) {
|
||||
$typeStr = '仅退款';
|
||||
$goodStatus = 'BUYER_NOT_RECEIVED';
|
||||
} else {
|
||||
$typeStr = '普通退货';
|
||||
$goodStatus = 'BUYER_RETURNED_GOODS';
|
||||
}
|
||||
|
||||
if ($applyType == 1) {
|
||||
$items = [];
|
||||
} elseif (is_array($items)) {
|
||||
foreach ($items as $idx => $it) {
|
||||
if (is_array($it)) {
|
||||
$it['type'] = '退货';
|
||||
$items[$idx] = $it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$biz = [
|
||||
'shop_id' => $shopid,
|
||||
'so_id' => (string)($order['order_id'] ?? ''),
|
||||
'outer_as_id' => $outerAsId,
|
||||
'type' => $typeStr,
|
||||
'good_status' => $goodStatus,
|
||||
'shop_status' => 'wait',
|
||||
'total_amount' => (float)($order['pay_price'] ?? 0),
|
||||
'payment' => 0.00,
|
||||
'question_type' => (string)($refund['refund_reason'] ?? $refund['refund_explain'] ?? ''),
|
||||
'refund' => (float)($refund['refund_price'] ?? $refund['apply_price'] ?? 0),
|
||||
'remark' => (string)($refund['remark'] ?? ''),
|
||||
'items' => $items,
|
||||
];
|
||||
|
||||
$this->retryPost($api, [$biz], 'jst.after_sale.create');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 售后单撤销推送(本地 -> 聚水潭)
|
||||
* @param int $refundId
|
||||
* @return bool
|
||||
*/
|
||||
public function afterSaleCancelPush(int $refundId): bool
|
||||
{
|
||||
$api = 'open/webapi/aftersaleapi/open/cancel';
|
||||
Log::info('jst.after_sale.cancel.start ' . json_encode(['refund_id' => $refundId], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
/** @var StoreOrderRefundServices $storeOrderRefundServices */
|
||||
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
|
||||
$refund = $storeOrderRefundServices->get($refundId);
|
||||
if (!$refund) {
|
||||
Log::warning('jst.after_sale.cancel.skip ' . json_encode(['refund_id' => $refundId, 'reason' => 'refund_missing'], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
if (method_exists($refund, 'toArray')) {
|
||||
$refund = $refund->toArray();
|
||||
}
|
||||
|
||||
/** @var StoreOrderServices $storeOrderServices */
|
||||
$storeOrderServices = app()->make(StoreOrderServices::class);
|
||||
$order = $storeOrderServices->get((int)($refund['store_order_id'] ?? 0), ['*'], ['store']);
|
||||
if (!$order) {
|
||||
Log::warning('jst.after_sale.cancel.skip ' . json_encode(['refund_id' => $refundId, 'reason' => 'order_missing'], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (($order['product_type'] ?? 0) != 4) {
|
||||
if (($order['product_type'] ?? 0) > 0 || !($order['paid'] ?? 0)) {
|
||||
Log::info('jst.after_sale.cancel.skip ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'reason' => 'product_filter',
|
||||
'product_type' => $order['product_type'] ?? null,
|
||||
'paid' => $order['paid'] ?? null,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$shopid = $this->resolveShopIdByOrder($order);
|
||||
if (!$shopid) {
|
||||
Log::info('jst.after_sale.cancel.skip ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'reason' => 'missing_shopid',
|
||||
'supplier_id' => $order['supplier_id'] ?? 0
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
|
||||
$outerAsId = (string)($refund['order_id'] ?? '');
|
||||
if ($outerAsId === '') {
|
||||
$outerAsId = (string)$refundId;
|
||||
}
|
||||
|
||||
$erpInfo = [];
|
||||
if (!empty($refund['erp'])) {
|
||||
$erpInfo = json_decode($refund['erp'], true) ?: [];
|
||||
}
|
||||
$asId = $erpInfo['as_id'] ?? 0;
|
||||
|
||||
if ($asId) {
|
||||
$biz = [
|
||||
'as_ids' => [(int)$asId],
|
||||
];
|
||||
} else {
|
||||
$biz = [
|
||||
'shop_id' => $shopid,
|
||||
'so_id' => (string)($order['order_id'] ?? ''),
|
||||
'outer_as_id' => $outerAsId,
|
||||
'reason' => (string)($refund['refuse_reason'] ?? $refund['refund_reason'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
$this->retryPost($api, $biz, 'jst.after_sale.cancel');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 售后单确认退款推送(本地 -> 聚水潭)
|
||||
* @param int $refundId
|
||||
* @return bool
|
||||
*/
|
||||
public function afterSaleRefundedPush(int $refundId): bool
|
||||
{
|
||||
$api = 'open/webapi/aftersaleapi/open/confirm';
|
||||
Log::info('jst.after_sale.refunded.start ' . json_encode(['refund_id' => $refundId], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
/** @var StoreOrderRefundServices $storeOrderRefundServices */
|
||||
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
|
||||
$refund = $storeOrderRefundServices->get($refundId);
|
||||
if (!$refund) {
|
||||
Log::warning('jst.after_sale.refunded.skip ' . json_encode(['refund_id' => $refundId, 'reason' => 'refund_missing'], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
if (method_exists($refund, 'toArray')) {
|
||||
$refund = $refund->toArray();
|
||||
}
|
||||
|
||||
/** @var StoreOrderServices $storeOrderServices */
|
||||
$storeOrderServices = app()->make(StoreOrderServices::class);
|
||||
$order = $storeOrderServices->get((int)($refund['store_order_id'] ?? 0), ['*'], ['store']);
|
||||
if (!$order) {
|
||||
Log::warning('jst.after_sale.refunded.skip ' . json_encode(['refund_id' => $refundId, 'reason' => 'order_missing'], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (($order['product_type'] ?? 0) != 4) {
|
||||
if (($order['product_type'] ?? 0) > 0 || !($order['paid'] ?? 0)) {
|
||||
Log::info('jst.after_sale.refunded.skip ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'reason' => 'product_filter',
|
||||
'product_type' => $order['product_type'] ?? null,
|
||||
'paid' => $order['paid'] ?? null,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$shopid = $this->resolveShopIdByOrder($order);
|
||||
if (!$shopid) {
|
||||
Log::info('jst.after_sale.refunded.skip ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'reason' => 'missing_shopid',
|
||||
'supplier_id' => $order['supplier_id'] ?? 0
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
|
||||
$outerAsId = (string)($refund['order_id'] ?? '');
|
||||
if ($outerAsId === '') {
|
||||
$outerAsId = (string)$refundId;
|
||||
}
|
||||
|
||||
$erpInfo = [];
|
||||
if (!empty($refund['erp'])) {
|
||||
$erpInfo = json_decode($refund['erp'], true) ?: [];
|
||||
}
|
||||
$asId = $erpInfo['as_id'] ?? 0;
|
||||
|
||||
if ($asId) {
|
||||
$biz = [
|
||||
'as_ids' => [(int)$asId],
|
||||
'exchange_force' => true,
|
||||
'confirm_refund' => true,
|
||||
];
|
||||
} else {
|
||||
$biz = [
|
||||
'shop_id' => $shopid,
|
||||
'so_id' => (string)($order['order_id'] ?? ''),
|
||||
'outer_as_id' => $outerAsId,
|
||||
'amount' => sprintf('%.2f', (float)($refund['refund_price'] ?? $refund['apply_price'] ?? 0)),
|
||||
];
|
||||
}
|
||||
|
||||
$this->retryPost($api, $biz, 'jst.after_sale.refunded');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getItems(int $oid, int $uid, string $unique, $rftype = ''): array
|
||||
{
|
||||
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
|
||||
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
|
||||
$cartInfo = $storeOrderCartInfoServices->getOrderCartInfo($oid);
|
||||
if (empty($cartInfo)) {
|
||||
/** @var StoreOrderServices $orderServices */
|
||||
$orderServices = app()->make(StoreOrderServices::class);
|
||||
$orderInfo = $orderServices->getCacheOrderInfo($uid, $unique);
|
||||
$cartInfo = $orderInfo['cartInfo'] ?? [];
|
||||
}
|
||||
$items = [];
|
||||
foreach ($cartInfo as $cart) {
|
||||
$cart = $cart['cart_info'] ?? $cart;
|
||||
$attrInfo = $cart['productInfo']['attrInfo'];
|
||||
$truePrice = (string)($cart['sum_true_price'] ?? $cart['truePrice']);
|
||||
$itemdet = [
|
||||
'sku_id' => $attrInfo['code'] ?: ($attrInfo['unique'] . 'ok'),
|
||||
'shop_sku_id' => $attrInfo['code'] ?: ($attrInfo['unique'] . 'ok'),
|
||||
'base_price' => (float)($attrInfo['price'] ?? 0),
|
||||
'price' => (float)bcdiv($truePrice, (string)$cart['cart_num'], 2),
|
||||
'amount' => (float)$truePrice,
|
||||
'qty' => $cart['cart_num'],
|
||||
'pic' => $cart['productInfo']['image'],
|
||||
'name' => $cart['productInfo']['store_name'],
|
||||
'properties_value' => $attrInfo['suk'],
|
||||
'outer_oi_id' => $cart['product_attr_unique'],
|
||||
'refund_qty' => $rftype == 'refund_nosend' ? $cart['cart_num'] : ($cart['refund_num'] ?? 0),
|
||||
];
|
||||
if ($itemdet['qty'] == $itemdet['refund_qty']) {
|
||||
$itemdet['refund_status'] = 'success';
|
||||
}
|
||||
$items[] = $itemdet;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function resolveShopIdByOrder($order): int
|
||||
{
|
||||
$rid = $order['supplier_id'] ?? 0;
|
||||
$shopid = (int)$this->getShopId();
|
||||
if ($rid) {
|
||||
/** @var SystemSupplierServices $systemSupplierServices */
|
||||
$systemSupplierServices = app()->make(SystemSupplierServices::class);
|
||||
$info = $systemSupplierServices->getSupplierInfo((int)$rid, 'erp_shopid');
|
||||
if (!$info || !$info['erp_shopid']) {
|
||||
return 0;
|
||||
}
|
||||
$shopid = (int)$info['erp_shopid'];
|
||||
}
|
||||
return (int)$shopid;
|
||||
}
|
||||
|
||||
private function buildAfterSaleItemsFromRefund(array $refund): array
|
||||
{
|
||||
$cartInfo = $refund['cart_info'] ?? [];
|
||||
if (is_string($cartInfo)) {
|
||||
$decoded = json_decode($cartInfo, true);
|
||||
if (is_array($decoded)) {
|
||||
$cartInfo = $decoded;
|
||||
}
|
||||
}
|
||||
if (!is_array($cartInfo)) {
|
||||
$cartInfo = [];
|
||||
}
|
||||
|
||||
$items = [];
|
||||
foreach ($cartInfo as $cart) {
|
||||
$cart = $cart['cart_info'] ?? $cart;
|
||||
$productInfo = $cart['productInfo'] ?? [];
|
||||
$attrInfo = $productInfo['attrInfo'] ?? [];
|
||||
$skuId = (string)($attrInfo['code'] ?? '');
|
||||
if ($skuId === '') {
|
||||
$skuId = (string)($attrInfo['unique'] ?? '');
|
||||
if ($skuId !== '') {
|
||||
$skuId .= 'ok';
|
||||
}
|
||||
}
|
||||
$items[] = [
|
||||
'sku_id' => $skuId,
|
||||
'qty' => (int)($cart['cart_num'] ?? 0),
|
||||
'price' => sprintf('%.2f', (float)($attrInfo['price'] ?? 0)),
|
||||
'amount' => sprintf('%.2f', (float)($cart['sum_true_price'] ?? 0)),
|
||||
'outer_oi_id' => (string)($cart['product_attr_unique'] ?? ''),
|
||||
'name' => (string)($productInfo['store_name'] ?? ''),
|
||||
];
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function retryPost(string $api, array $biz, string $logPrefix): void
|
||||
{
|
||||
$meta = $this->extractBizMeta($biz);
|
||||
$num = 3;
|
||||
for ($i = $num; $i >= 0; $i--) {
|
||||
if ($i <= 0) {
|
||||
Log::error($logPrefix . '.failed ' . json_encode(array_merge([
|
||||
'api' => $api,
|
||||
], $meta), JSON_UNESCAPED_UNICODE));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$rt = $this->post($api, $biz);
|
||||
return;
|
||||
} catch (\Throwable $e) {
|
||||
usleep(1000 * 50);
|
||||
Log::error($logPrefix . '.error ' . $e->getMessage() . ' ' . json_encode(array_merge([
|
||||
'try' => ($num - $i + 1),
|
||||
'api' => $api,
|
||||
], $meta), JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function extractBizMeta(array $biz): array
|
||||
{
|
||||
$first = $biz;
|
||||
if ($this->isListArray($biz) && isset($biz[0]) && is_array($biz[0])) {
|
||||
$first = $biz[0];
|
||||
}
|
||||
if (!is_array($first)) {
|
||||
return [];
|
||||
}
|
||||
$meta = [];
|
||||
foreach (['shop_id', 'so_id', 'outer_as_id', 'as_ids'] as $k) {
|
||||
if (array_key_exists($k, $first)) {
|
||||
$meta[$k] = $first[$k];
|
||||
}
|
||||
}
|
||||
return $meta;
|
||||
}
|
||||
|
||||
private function isListArray(array $arr): bool
|
||||
{
|
||||
if ($arr === []) {
|
||||
return true;
|
||||
}
|
||||
return array_keys($arr) === range(0, count($arr) - 1);
|
||||
}
|
||||
|
||||
private function getAddress($address): array
|
||||
{
|
||||
if (is_string($address)) {
|
||||
$decoded = json_decode($address, true);
|
||||
if (is_array($decoded)) {
|
||||
$address = $decoded;
|
||||
} else {
|
||||
$parts = preg_split('/\s+/u', trim($address));
|
||||
if (is_array($parts) && count($parts) >= 3) {
|
||||
$address = [
|
||||
'province' => $parts[0] ?? '',
|
||||
'city' => $parts[1] ?? '',
|
||||
'area' => $parts[2] ?? '',
|
||||
'detail' => trim(implode(' ', array_slice($parts, 3))),
|
||||
];
|
||||
if (($address['detail'] ?? '') === '') {
|
||||
$address['detail'] = $parts[2] ?? '';
|
||||
}
|
||||
} else {
|
||||
$address = ['detail' => $address];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_array($address)) {
|
||||
$address = [];
|
||||
}
|
||||
return [
|
||||
'receiver_address' => $address['detail'] ?? '',
|
||||
'receiver_state' => $address['province'] ?? '',
|
||||
'receiver_city' => $address['city'] ?? '',
|
||||
'receiver_district' => $address['area'] ?? '',
|
||||
'receiver_zip' => $address['post_code'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
public static function getServices(): HaichaoJstServices
|
||||
{
|
||||
return new HaichaoJstServices();
|
||||
}
|
||||
|
||||
public function getProductListShop($key = ''): array
|
||||
{
|
||||
$time = 1;
|
||||
$data = $this->post('open/skumap/query', [
|
||||
'modified_begin' => date('Y-m-d H:i', (time() - (86400 * $time))),
|
||||
'modified_end' => date('Y-m-d H:i', time() - (86400 * $time) + 7 * 86400),
|
||||
'page_index' => 1,
|
||||
'page_size' => 50,
|
||||
'shop_id' => $key
|
||||
])['data'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getProductListName($key = ''): array
|
||||
{
|
||||
$data = $this->post('open/sku/query', [
|
||||
'exactly_name' => $key,
|
||||
'page_index' => 1,
|
||||
'page_size' => 10,
|
||||
])['data'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function syncProductSingle($data, $pid = 0)
|
||||
{
|
||||
$jstProductSync = app()->make(\app\haichao\sync\JstProductSync::class);
|
||||
return $jstProductSync->syncProductSingle($data, $pid);
|
||||
}
|
||||
|
||||
public function getProductListSku($key = ''): array
|
||||
{
|
||||
$data = $this->post('open/sku/query', [
|
||||
'sku_ids' => $key,
|
||||
'page_index' => 1,
|
||||
'page_size' => 10,
|
||||
])['data'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getProductListKuan($key = ''): array
|
||||
{
|
||||
$data = $this->post('open/mall/item/query', [
|
||||
'i_ids' => [$key],
|
||||
'page_index' => 1,
|
||||
'page_size' => 10,
|
||||
'date_field' => '',
|
||||
])['data'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getProductListStand(): array
|
||||
{
|
||||
$time = 10;
|
||||
$data = $this->post('open/mall/item/query', [
|
||||
'modified_begin' => date('Y-m-d H:i', (time() - (86400 * $time))),
|
||||
'modified_end' => date('Y-m-d H:i', time() - (86400 * $time) + 7 * 86400),
|
||||
'i_ids' => [],
|
||||
'page_index' => 1,
|
||||
'page_size' => 50,
|
||||
'date_field' => '',
|
||||
])['data'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getProductList(): array
|
||||
{
|
||||
$time = 1;
|
||||
if (file_exists(root_path() . 'runtime/jstdata/synctime.txt')) {
|
||||
$time = file_get_contents(root_path() . 'runtime/jstdata/synctime.txt');
|
||||
}
|
||||
|
||||
$syncint = 0;
|
||||
if (file_exists(root_path() . 'runtime/jstdata/syncint.txt')) {
|
||||
$syncint = file_get_contents(root_path() . 'runtime/jstdata/syncint.txt');
|
||||
}
|
||||
$syncint++; // 这里的 $syncint 实际上代表要拉取的页码(page_index)
|
||||
|
||||
$data = $this->post('open/mall/item/query', [
|
||||
'modified_begin' => date('Y-m-d H:i', (time() - (86400 * $time))),
|
||||
'modified_end' => date('Y-m-d H:i', time() - (86400 * $time) + 7 * 86400),
|
||||
'i_ids' => [],
|
||||
'page_index' => $syncint, // 将写死的 1 改为动态的页码变量
|
||||
'page_size' => 10,
|
||||
'date_field' => '',
|
||||
])['data'];
|
||||
|
||||
if ($syncint > $data['page_count']) {
|
||||
$syncint = 1;
|
||||
$time += 7;
|
||||
if ($time > 500) {
|
||||
$time = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_dir(root_path() . 'runtime/jstdata')) {
|
||||
mkdir(root_path() . 'runtime/jstdata', 0777, true);
|
||||
}
|
||||
file_put_contents(root_path() . 'runtime/jstdata/syncint.txt', $syncint);
|
||||
file_put_contents(root_path() . 'runtime/jstdata/synctime.txt', $time);
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getStork(string $ids): array
|
||||
{
|
||||
return $this->post('open/inventory/query', [
|
||||
'page_index' => 1,
|
||||
'page_size' => 100,
|
||||
'sku_ids' => $ids,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getInneropenItemList(): array
|
||||
{
|
||||
$return = [];
|
||||
$n = 1;
|
||||
if (file_exists(root_path() . 'runtime/jstdata/syncintgood.txt')) {
|
||||
$n = file_get_contents(root_path() . 'runtime/jstdata/syncintgood.txt');
|
||||
}
|
||||
$data = $this->post('open/api/goods/inneropen/goods/querygoodslist', [
|
||||
'page_num' => $n,
|
||||
'page_size' => 10,
|
||||
'sort' => 'ASC'
|
||||
])['data'];
|
||||
$result = $data['list'];
|
||||
$return = array_merge($result, $return);
|
||||
if (file_exists(root_path() . 'runtime/jstdata/syncintgood.txt')) {
|
||||
$n = file_get_contents(root_path() . 'runtime/jstdata/syncintgood.txt');
|
||||
$n++;
|
||||
if ($n > ceil($data['total'] / 10)) {
|
||||
$n = 1;
|
||||
}
|
||||
}
|
||||
if (!is_dir(root_path() . 'runtime/jstdata')) {
|
||||
mkdir(root_path() . 'runtime/jstdata', 0777, true);
|
||||
}
|
||||
file_put_contents(root_path() . 'runtime/jstdata/syncintgood.txt', $n);
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function getInneropenItemDetail($skuId): array
|
||||
{
|
||||
return $this->post('open/api/goods/inneropen/goods/querygoodsdetail', [
|
||||
'item_spuId' => $skuId
|
||||
])['data'];
|
||||
}
|
||||
|
||||
public function findStockBySkuId($skuid = ''): array
|
||||
{
|
||||
$result = $this->post('open/inventory/query', [
|
||||
'page_num' => 1,
|
||||
'page_size' => 500,
|
||||
'sku_ids' => $skuid,
|
||||
])['data']['inventorys'];
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getInneropenStockAndDetailList($styleCodes): array
|
||||
{
|
||||
$rt = [];
|
||||
$i = 1;
|
||||
do {
|
||||
$result = $this->post('open/inventory/query', [
|
||||
'page_index' => $i++,
|
||||
'page_size' => 100,
|
||||
'sku_ids' => $styleCodes
|
||||
])['data']['inventorys'] ?? [];
|
||||
|
||||
// 适配原来的返回格式结构
|
||||
$formatResult = [];
|
||||
foreach ($result as $item) {
|
||||
$formatResult[] = [
|
||||
'item_code' => $item['sku_id'],
|
||||
'stock' => max(0, ($item['qty'] ?? 0) - ($item['order_lock'] ?? 0)) // 实际可用库存
|
||||
];
|
||||
}
|
||||
$rt = array_merge($formatResult, $rt);
|
||||
usleep(550);
|
||||
} while (count($result) == 100);
|
||||
return $rt;
|
||||
}
|
||||
|
||||
public function findAllInneropenItemBySkuId(string $skuIds): array
|
||||
{
|
||||
return $this->getInneropenStockAndDetailList($skuIds);
|
||||
}
|
||||
|
||||
public function getInneropenList(int $status = 2): array
|
||||
{
|
||||
$rt = $this->post('open/api/company/inneropen/partner/channel/querymysupplier', [
|
||||
'page_num' => 1,
|
||||
'page_size' => 100,
|
||||
])['data']['supplier_vos'];
|
||||
$rtr = [];
|
||||
foreach ($rt as $inneropen) {
|
||||
if ($inneropen['status'] == $status) {
|
||||
$rtr[] = $inneropen;
|
||||
}
|
||||
}
|
||||
return $rtr;
|
||||
}
|
||||
|
||||
public function getSyncProduct(): \app\haichao\sync\JstProductSync
|
||||
{
|
||||
return new \app\haichao\sync\JstProductSync();
|
||||
}
|
||||
|
||||
public function getSyncStork(): \app\haichao\sync\JstStorkSync
|
||||
{
|
||||
return new \app\haichao\sync\JstStorkSync();
|
||||
}
|
||||
}
|
||||
40
app/haichao/callback/JstCallbackEventManager.php
Normal file
40
app/haichao/callback/JstCallbackEventManager.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\callback;
|
||||
|
||||
use app\haichao\callback\event\JstEvent;
|
||||
use app\haichao\callback\event\JstOrderPostEvent;
|
||||
use app\haichao\callback\event\JstOrderCancelEvent;
|
||||
use app\haichao\callback\event\JstOrderRefundEvent;
|
||||
use app\haichao\callback\event\JstProductEvent;
|
||||
use app\haichao\callback\event\JstTradeEvent;
|
||||
use app\Request;
|
||||
use ReflectionClass;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
use think\response\Json;
|
||||
|
||||
abstract class JstCallbackEventManager
|
||||
{
|
||||
public static array $eventList = [
|
||||
'business_sku_syn' => JstProductEvent::class,
|
||||
'trade_after_sale' => JstTradeEvent::class,
|
||||
'orderCancel' => JstOrderCancelEvent::class,
|
||||
'orderPost' => JstOrderPostEvent::class,
|
||||
'orderRefund' => JstOrderRefundEvent::class,
|
||||
];
|
||||
|
||||
public static function callback(Request $request): Json
|
||||
{
|
||||
$actionCode = $request->getMore([['action_code', false]])['action_code'] ?? '';
|
||||
Log::info('JST callback ' . $actionCode);
|
||||
if ($actionCode && isset(self::$eventList[$actionCode])) {
|
||||
$reflectionClass = new ReflectionClass(self::$eventList[$actionCode]);
|
||||
/** @var JstEvent $eventObj */
|
||||
$eventObj = $reflectionClass->newInstance();
|
||||
$eventObj->event($request);
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
throw new ValidateException('unknown action_code');
|
||||
}
|
||||
}
|
||||
10
app/haichao/callback/event/JstEvent.php
Normal file
10
app/haichao/callback/event/JstEvent.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\callback\event;
|
||||
|
||||
use app\Request;
|
||||
|
||||
interface JstEvent
|
||||
{
|
||||
public function event(Request $request);
|
||||
}
|
||||
23
app/haichao/callback/event/JstOrderCancelEvent.php
Normal file
23
app/haichao/callback/event/JstOrderCancelEvent.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\callback\event;
|
||||
|
||||
use app\Request;
|
||||
use app\services\erp\OrderServices;
|
||||
|
||||
class JstOrderCancelEvent implements JstEvent
|
||||
{
|
||||
public function event(Request $request): array
|
||||
{
|
||||
$soId = $request->getMore([['so_id', false]])['so_id'];
|
||||
if ($soId) {
|
||||
/** @var OrderServices $orderServices */
|
||||
$orderServices = app()->make(OrderServices::class);
|
||||
$orderServices->cancelCallback($request->postMore([
|
||||
['so_id', 0],
|
||||
['remark', ''],
|
||||
]));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
296
app/haichao/callback/event/JstOrderPostEvent.php
Normal file
296
app/haichao/callback/event/JstOrderPostEvent.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\callback\event;
|
||||
|
||||
use app\Request;
|
||||
use app\services\order\StoreOrderCartInfoServices;
|
||||
use app\services\order\StoreOrderDeliveryServices;
|
||||
use app\services\order\StoreOrderRefundServices;
|
||||
use app\services\order\StoreOrderServices;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
|
||||
class JstOrderPostEvent implements JstEvent
|
||||
{
|
||||
public function event(Request $request)
|
||||
{
|
||||
$payload = [];
|
||||
$content = (string)$request->getContent();
|
||||
if ($content !== '') {
|
||||
$decoded = json_decode($content, true);
|
||||
if (is_array($decoded)) {
|
||||
$payload = $decoded;
|
||||
} else {
|
||||
$output = [];
|
||||
parse_str($content, $output);
|
||||
if (is_array($output)) {
|
||||
$payload = $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$payload) {
|
||||
$payload = $request->param();
|
||||
}
|
||||
|
||||
$biz = [];
|
||||
if (isset($payload['biz']) && $payload['biz'] !== '') {
|
||||
if (is_array($payload['biz'])) {
|
||||
$biz = $payload['biz'];
|
||||
} else {
|
||||
$bizDecoded = json_decode((string)$payload['biz'], true);
|
||||
if (is_array($bizDecoded)) {
|
||||
$biz = $bizDecoded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oId = $request->getMore([['o_id', false]])['o_id'] ?: (string)($payload['o_id'] ?? $biz['o_id'] ?? ($payload['oId'] ?? $biz['oId'] ?? ''));
|
||||
$logisticsCompany = $request->getMore([['logistics_company', false]])['logistics_company'] ?: (string)($payload['logistics_company'] ?? $biz['logistics_company'] ?? ($payload['logisticsCompany'] ?? $biz['logisticsCompany'] ?? ''));
|
||||
$lId = $request->getMore([['l_id', false]])['l_id'] ?: (string)($payload['l_id'] ?? $biz['l_id'] ?? ($payload['lId'] ?? $biz['lId'] ?? ''));
|
||||
$lcId = $request->getMore([['lc_id', false]])['lc_id'] ?: (string)($payload['lc_id'] ?? $biz['lc_id'] ?? ($payload['lcId'] ?? $biz['lcId'] ?? ''));
|
||||
$soId = $request->getMore([['so_id', false]])['so_id'] ?: (string)($payload['so_id'] ?? $biz['so_id'] ?? ($payload['soId'] ?? $biz['soId'] ?? ''));
|
||||
$items = $request->getMore([['items', false]])['items'] ?: ($payload['items'] ?? $biz['items'] ?? ($payload['item_list'] ?? $biz['item_list'] ?? []));
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
$skus = [];
|
||||
if (is_array($items)) {
|
||||
foreach ($items as $it) {
|
||||
$skus[] = ['sku' => $it['sku_id'] ?? '', 'qty' => (int)($it['qty'] ?? 0)];
|
||||
}
|
||||
}
|
||||
Log::info('jst.cb.orderPost.start ' . json_encode([
|
||||
'so_id' => $soId,
|
||||
'o_id' => $oId,
|
||||
'logistics_company' => $logisticsCompany,
|
||||
'l_id' => $lId,
|
||||
'lc_id' => $lcId,
|
||||
'item_count' => is_array($items) ? count($items) : 0,
|
||||
'sku_list' => $skus,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
/** @var StoreOrderServices $storeOrderServices */
|
||||
$storeOrderServices = app()->make(StoreOrderServices::class);
|
||||
/** @var StoreOrderDeliveryServices $orderDeliverServices */
|
||||
$orderDeliverServices = app()->make(StoreOrderDeliveryServices::class);
|
||||
/** @var StoreOrderRefundServices $storeOrderRefundServices */
|
||||
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
|
||||
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
|
||||
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
|
||||
|
||||
if ($soId === '') {
|
||||
Log::info('jst.cb.orderPost.skip ' . json_encode(['reason' => 'missing so_id'], JSON_UNESCAPED_UNICODE));
|
||||
return 'missing so_id';
|
||||
}
|
||||
|
||||
$orderInfo = $storeOrderServices->getOne(['order_id' => $soId]);
|
||||
if (!$orderInfo) throw new ValidateException('order not found ' . $soId);
|
||||
$oid = $orderInfo['id'];
|
||||
$splitOrderList = $storeOrderServices->getSplitOrderList(['pid' => $oid]);
|
||||
$nol = [];
|
||||
foreach ($splitOrderList as $splitOrder) {
|
||||
if ($splitOrder['status'] === 0) $nol[] = $splitOrder;
|
||||
}
|
||||
$splitOrderList = $nol;
|
||||
Log::info('jst.cb.orderPost.split.count ' . json_encode(['so_id' => $soId, 'count' => count($splitOrderList)], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$cardIds = $orderInfo['cart_id'];
|
||||
$num = 0;
|
||||
foreach ($cardIds as $cardId) {
|
||||
$cardInfo = $storeOrderCartInfoServices->getOne(['cart_id' => $cardId])->toArray();
|
||||
$num += $cardInfo['cart_num'];
|
||||
}
|
||||
$num2 = 0;
|
||||
foreach ($items as $item) {
|
||||
$num2 += $item['qty'];
|
||||
}
|
||||
if ($num !== $num2) {
|
||||
Log::warning('jst.cb.orderPost.qty_mismatch ' . json_encode(['so_id' => $soId, 'order_cart_num' => $num, 'ship_qty_sum' => $num2], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
$existSo = false;
|
||||
if ($splitOrderList) {
|
||||
foreach ($splitOrderList as $splitOrder) {
|
||||
if ($splitOrder['order_id'] == $soId) {
|
||||
$existSo = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$existSo = true;
|
||||
}
|
||||
if (!$existSo) {
|
||||
Log::warning('jst.cb.orderPost.no_exist_so ' . json_encode(['so_id' => $soId], JSON_UNESCAPED_UNICODE));
|
||||
foreach ($splitOrderList as $item) {
|
||||
foreach ($item['cart_id'] as $cartId) {
|
||||
$cartInfo = $item['_info'][$cartId]['cart_info'];
|
||||
$productInfo = $cartInfo['productInfo'];
|
||||
$cartfa = [];
|
||||
foreach ($items as $sentProduct) {
|
||||
if ($productInfo['attrInfo']['suk'] == $sentProduct['sku_id'] ||
|
||||
$productInfo['attrInfo']['code'] == $sentProduct['sku_id'] ||
|
||||
($productInfo['attrInfo']['unique'] . 'ok') == $sentProduct['sku_id']) {
|
||||
$cartfa[] = [
|
||||
'cart_id' => $cartId,
|
||||
'cart_num' => $sentProduct['qty'],
|
||||
];
|
||||
}
|
||||
}
|
||||
try {
|
||||
$deliverArgs = [
|
||||
'type' => 1,
|
||||
'delivery_name' => $logisticsCompany,
|
||||
'delivery_id' => $lId,
|
||||
'delivery_code' => $lcId,
|
||||
'express_record_type' => "1",
|
||||
'express_temp_id' => md5(time()),
|
||||
'cart_ids' => $cartfa
|
||||
];
|
||||
$orderDeliverServices->splitDelivery($item['id'], $deliverArgs, 0, true);
|
||||
Log::info('jst.cb.orderPost.success ' . json_encode(['so_id' => $soId, 'oid' => (int)$item['id'], 'mode' => 'split'], JSON_UNESCAPED_UNICODE));
|
||||
return 'ok';
|
||||
} catch (\Throwable $exception) {
|
||||
Log::error('jst.cb.orderPost.split.error ' . json_encode(['oid' => $item['id'], 'msg' => $exception->getMessage()], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException('split delivery error ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::warning('jst.cb.orderPost.no_match.split ' . json_encode(['so_id' => $soId], JSON_UNESCAPED_UNICODE));
|
||||
return 'no match';
|
||||
}
|
||||
try {
|
||||
if (count($splitOrderList) > 1 || count($orderInfo['cart_id']) > 1) {
|
||||
if (count($splitOrderList) > 0) {
|
||||
foreach ($splitOrderList as $item) {
|
||||
foreach ($item['cart_id'] as $cartId) {
|
||||
$cartInfo = $item['_info'][$cartId]['cart_info'];
|
||||
$productInfo = $cartInfo['productInfo'];
|
||||
foreach ($items as $postItem) {
|
||||
$postItemSku = $postItem['sku_id'];
|
||||
if ($productInfo['attrInfo']['suk'] == $postItemSku || $productInfo['attrInfo']['code'] == $postItemSku) {
|
||||
try {
|
||||
$deliverArgs = [
|
||||
'type' => 1,
|
||||
'delivery_name' => $logisticsCompany,
|
||||
'delivery_id' => $lId,
|
||||
'delivery_code' => $lcId,
|
||||
'express_record_type' => "1",
|
||||
'express_temp_id' => md5(time()),
|
||||
'cart_ids' => [
|
||||
[
|
||||
'cart_id' => $cartId,
|
||||
'cart_num' => $cartInfo['cart_num'],
|
||||
]
|
||||
]
|
||||
];
|
||||
$orderDeliverServices->splitDelivery($oid, $deliverArgs, 0, true);
|
||||
Log::info('jst.cb.orderPost.success ' . json_encode(['so_id' => $soId, 'oid' => (int)$oid, 'mode' => 'split.pid', 'cart_id' => (int)$cartId], JSON_UNESCAPED_UNICODE));
|
||||
return 'ok';
|
||||
} catch (\Throwable $exception) {
|
||||
Log::error('jst.cb.orderPost.split.pid.error ' . json_encode(['pid' => $oid, 'cart_id' => $cartId, 'msg' => $exception->getMessage()], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException('split delivery error ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::warning('jst.cb.orderPost.no_match.pid ' . json_encode(['so_id' => $soId, 'pid' => (int)$oid], JSON_UNESCAPED_UNICODE));
|
||||
return 'no match';
|
||||
}
|
||||
if (count($orderInfo['cart_id']) > 1) {
|
||||
$cartfa = [];
|
||||
foreach ($items as $sentProduct) {
|
||||
foreach ($orderInfo['cart_id'] as $cartId) {
|
||||
$cartInfo = $storeOrderCartInfoServices->getOne(['cart_id' => $cartId]);
|
||||
$productInfo = $cartInfo['cart_info']['productInfo'] ?? [];
|
||||
if (!$productInfo) continue;
|
||||
if ($productInfo['attrInfo']['suk'] == $sentProduct['sku_id'] ||
|
||||
$productInfo['attrInfo']['code'] == $sentProduct['sku_id'] ||
|
||||
($productInfo['attrInfo']['unique'] . 'ok') == $sentProduct['sku_id']) {
|
||||
$cartfa[] = [
|
||||
'cart_id' => $cartId,
|
||||
'cart_num' => $cartInfo['cart_num'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::info('jst.cb.orderPost.multi.mapping ' . json_encode(['pid' => $oid, 'match' => $cartfa], JSON_UNESCAPED_UNICODE));
|
||||
try {
|
||||
$deliverArgs = [
|
||||
'type' => 1,
|
||||
'delivery_id' => $lId,
|
||||
'delivery_code' => $lcId,
|
||||
'express_record_type' => "1",
|
||||
'express_temp_id' => md5(time()),
|
||||
'cart_ids' => $cartfa
|
||||
];
|
||||
$orderDeliverServices->splitDelivery($oid, $deliverArgs, 0, true);
|
||||
Log::info('jst.cb.orderPost.success ' . json_encode(['so_id' => $soId, 'oid' => (int)$oid, 'mode' => 'multi'], JSON_UNESCAPED_UNICODE));
|
||||
return 'ok';
|
||||
} catch (\Throwable $exception) {
|
||||
Log::error('jst.cb.orderPost.multi.error ' . json_encode(['pid' => $oid, 'msg' => $exception->getMessage()], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException('split delivery error ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($num == $num2) {
|
||||
$deliverArgs = [
|
||||
'type' => 1,
|
||||
'delivery_name' => $logisticsCompany,
|
||||
'delivery_id' => $lId,
|
||||
'delivery_code' => $lcId,
|
||||
'express_record_type' => "1",
|
||||
'express_temp_id' => md5(time()),
|
||||
];
|
||||
$orderDeliverServices->delivery($oid, $deliverArgs);
|
||||
Log::info('jst.cb.orderPost.success ' . json_encode(['so_id' => $soId, 'oid' => (int)$oid, 'mode' => 'delivery'], JSON_UNESCAPED_UNICODE));
|
||||
return 'ok';
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
if ($e->getMessage() != '订单已发货请勿重复操作!') {
|
||||
Log::error('jst.cb.orderPost.error ' . json_encode(['oid' => $oid, 'msg' => $e->getMessage()], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException($e->getMessage());
|
||||
}
|
||||
Log::info('jst.cb.orderPost.repeat ' . json_encode(['so_id' => $soId, 'oid' => (int)$oid], JSON_UNESCAPED_UNICODE));
|
||||
return 'ok';
|
||||
}
|
||||
$cardIds = $orderInfo['cart_id'];
|
||||
if (count($cardIds) >= 1) {
|
||||
$cartfa = [];
|
||||
foreach ($cardIds as $cartId) {
|
||||
$cardInfo = $storeOrderCartInfoServices->getOne(['cart_id' => $cartId])->toArray();
|
||||
$productInfo = $cardInfo['cart_info']['productInfo'] ?? [];
|
||||
foreach ($items as $sentProduct) {
|
||||
if ($productInfo['attrInfo']['suk'] == $sentProduct['sku_id'] ||
|
||||
$productInfo['attrInfo']['code'] == $sentProduct['sku_id'] ||
|
||||
($productInfo['attrInfo']['unique'] . 'ok') == $sentProduct['sku_id']) {
|
||||
$cartfa[] = [
|
||||
'cart_id' => $cartId,
|
||||
'cart_num' => $sentProduct['qty'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
$deliverArgs = [
|
||||
'type' => 1,
|
||||
'delivery_name' => $logisticsCompany,
|
||||
'delivery_id' => $lId,
|
||||
'delivery_code' => $lcId,
|
||||
'express_record_type' => "1",
|
||||
'express_temp_id' => md5(time()),
|
||||
'cart_ids' => $cartfa
|
||||
];
|
||||
$orderDeliverServices->splitDelivery($oid, $deliverArgs);
|
||||
Log::info('jst.cb.orderPost.success ' . json_encode(['so_id' => $soId, 'oid' => (int)$oid, 'mode' => 'cartfa'], JSON_UNESCAPED_UNICODE));
|
||||
return 'ok';
|
||||
} catch (\Throwable $exception) {
|
||||
Log::error('jst.cb.orderPost.cartfa.error ' . json_encode(['pid' => $oid, 'msg' => $exception->getMessage()], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException('split delivery error ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
Log::warning('jst.cb.orderPost.unknown ' . json_encode(['so_id' => $soId], JSON_UNESCAPED_UNICODE));
|
||||
throw new ValidateException('unknown delivery ' . $soId);
|
||||
}
|
||||
}
|
||||
27
app/haichao/callback/event/JstOrderRefundEvent.php
Normal file
27
app/haichao/callback/event/JstOrderRefundEvent.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\callback\event;
|
||||
|
||||
use app\Request;
|
||||
use app\services\order\StoreOrderServices;
|
||||
|
||||
class JstOrderRefundEvent implements JstEvent
|
||||
{
|
||||
public function event(Request $request): array
|
||||
{
|
||||
$actionName = $request->getMore(['action_name'])['action_name'];
|
||||
switch ($actionName) {
|
||||
case '确认收货':
|
||||
{
|
||||
/** @var StoreOrderServices $storeOrderServices */
|
||||
$storeOrderServices = app()->make(StoreOrderServices::class);
|
||||
$orderInfo = $storeOrderServices->getOne(['erp_id' => $request->getMore([['o_id', -1]])['o_id']]);
|
||||
if ($orderInfo) {
|
||||
$storeOrderServices->update(['id' => $orderInfo['id']], ['status' => 3]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
48
app/haichao/callback/event/JstProductEvent.php
Normal file
48
app/haichao/callback/event/JstProductEvent.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\callback\event;
|
||||
|
||||
use app\Request;
|
||||
use app\services\product\sku\StoreProductAttrValueServices;
|
||||
use think\facade\Log;
|
||||
use Throwable;
|
||||
|
||||
class JstProductEvent implements JstEvent
|
||||
{
|
||||
public function event(Request $request): array
|
||||
{
|
||||
try {
|
||||
$param = $request->param();
|
||||
$body = json_decode((string)($param['biz'] ?? '{}'), true);
|
||||
$skuId = (string)($body['skuId'] ?? '');
|
||||
$stock = (int)($body['qty'] ?? 0);
|
||||
if (!$skuId) {
|
||||
$cont = $request->getContent();
|
||||
$output = [];
|
||||
parse_str($cont, $output);
|
||||
$body = json_decode((string)($output['biz'] ?? '{}'), true);
|
||||
$skuId = (string)($body['skuId'] ?? '');
|
||||
$stock = (int)($body['qty'] ?? 0);
|
||||
}
|
||||
if (!$skuId) return [];
|
||||
/** @var StoreProductAttrValueServices $spas */
|
||||
$spas = app()->make(StoreProductAttrValueServices::class);
|
||||
$model = $spas->getOne(['code' => $skuId]);
|
||||
if ($model) {
|
||||
$pdt = $model->toArray();
|
||||
$id = $pdt['id'] ?? 0;
|
||||
if ($id) {
|
||||
$spas->update($id, [
|
||||
'stock' => $stock,
|
||||
'sum_stock' => $stock
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
Log::error('jst.cb.product_stock.error ' . json_encode([
|
||||
'msg' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
298
app/haichao/callback/event/JstTradeEvent.php
Normal file
298
app/haichao/callback/event/JstTradeEvent.php
Normal file
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\callback\event;
|
||||
|
||||
use app\Request;
|
||||
use app\services\erp\OrderServices;
|
||||
use app\services\order\StoreOrderCartInfoServices;
|
||||
use app\services\order\StoreOrderRefundServices;
|
||||
use app\services\order\StoreOrderServices;
|
||||
use think\facade\Log;
|
||||
|
||||
class JstTradeEvent implements JstEvent
|
||||
{
|
||||
public function event(Request $request): array
|
||||
{
|
||||
$payload = [];
|
||||
$content = (string)$request->getContent();
|
||||
if ($content !== '') {
|
||||
$decoded = json_decode($content, true);
|
||||
if (is_array($decoded)) {
|
||||
$payload = $decoded;
|
||||
} else {
|
||||
$output = [];
|
||||
parse_str($content, $output);
|
||||
if (is_array($output)) {
|
||||
$payload = $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$payload) {
|
||||
$payload = $request->param();
|
||||
}
|
||||
|
||||
$biz = [];
|
||||
if (isset($payload['biz']) && $payload['biz'] !== '') {
|
||||
if (is_array($payload['biz'])) {
|
||||
$biz = $payload['biz'];
|
||||
} else {
|
||||
$bizDecoded = json_decode((string)$payload['biz'], true);
|
||||
if (is_array($bizDecoded)) {
|
||||
$biz = $bizDecoded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$actionName = (string)($payload['action_name'] ?? $biz['action_name'] ?? ($payload['action'] ?? $biz['action'] ?? ''));
|
||||
$shopStatus = strtoupper((string)($payload['shop_status'] ?? $biz['shop_status'] ?? ($payload['shopStatus'] ?? $biz['shopStatus'] ?? '')));
|
||||
$outerAsId = (string)($payload['outer_as_id'] ?? $biz['outer_as_id'] ?? ($payload['outerAsId'] ?? $biz['outerAsId'] ?? ''));
|
||||
$soId = (string)($payload['so_id'] ?? $biz['so_id'] ?? ($payload['soId'] ?? $biz['soId'] ?? ''));
|
||||
$bizStatus = strtoupper((string)($biz['status'] ?? $payload['status'] ?? ''));
|
||||
$bizType = (string)($biz['type'] ?? '');
|
||||
$bizRemark = (string)($biz['remark'] ?? '');
|
||||
|
||||
Log::info('jst.cb.trade_after_sale.start ' . json_encode([
|
||||
'action_name' => $actionName,
|
||||
'shop_status' => $shopStatus,
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'biz_status' => $bizStatus,
|
||||
'biz_type' => $bizType,
|
||||
'content_len' => strlen($content),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
if ($outerAsId === '') {
|
||||
Log::info('jst.cb.trade_after_sale.skip ' . json_encode([
|
||||
'reason' => 'missing outer_as_id',
|
||||
'shop_status' => $shopStatus,
|
||||
'action_name' => $actionName,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$applyType = 1;
|
||||
if ($bizType !== '') {
|
||||
if (mb_strpos($bizType, '仅退款') !== false) {
|
||||
$applyType = 1;
|
||||
} elseif (mb_strpos($bizType, '退货') !== false) {
|
||||
$applyType = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var StoreOrderServices $storeOrderServices */
|
||||
$storeOrderServices = app()->make(StoreOrderServices::class);
|
||||
$soIdsToTry = array_values(array_filter(array_unique([
|
||||
$soId,
|
||||
(string)(explode('_', $soId)[0] ?? ''),
|
||||
])));
|
||||
$order = null;
|
||||
foreach ($soIdsToTry as $trySoId) {
|
||||
$order = $storeOrderServices->getOne(['erp_order_id' => $trySoId], 'id,uid,store_id,supplier_id,order_id,erp_order_id,total_num,pay_price,pay_postage,channel,pid');
|
||||
if ($order) break;
|
||||
$order = $storeOrderServices->getOne(['order_id' => $trySoId], 'id,uid,store_id,supplier_id,order_id,erp_order_id,total_num,pay_price,pay_postage,channel,pid');
|
||||
if ($order) break;
|
||||
}
|
||||
if (!$order) {
|
||||
Log::error('jst.cb.trade_after_sale.order_missing ' . json_encode([
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'biz_status' => $bizStatus,
|
||||
'biz_type' => $bizType,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var StoreOrderRefundServices $storeOrderRefundServices */
|
||||
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
|
||||
$orderRefund = $storeOrderRefundServices->getOne(['order_id' => $outerAsId]);
|
||||
if (!$orderRefund) {
|
||||
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
|
||||
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
|
||||
$items = $biz['items'] ?? [];
|
||||
$skuQtyMap = [];
|
||||
if (is_array($items)) {
|
||||
foreach ($items as $item) {
|
||||
if (!is_array($item)) continue;
|
||||
$sku = (string)($item['sku_id'] ?? $item['skuId'] ?? '');
|
||||
$qty = (int)($item['qty'] ?? 0);
|
||||
if ($sku !== '' && $qty > 0) {
|
||||
$skuQtyMap[$sku] = ($skuQtyMap[$sku] ?? 0) + $qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$targetOrder = $order;
|
||||
$leftSkuQtyMap = $skuQtyMap;
|
||||
$cartInfos = $storeOrderCartInfoServices->getCartColunm(['oid' => (int)$order['id']], 'id,cart_id,cart_num,cart_info');
|
||||
foreach ($cartInfos as &$cartInfo) {
|
||||
$cartInfo['cart_info'] = is_string($cartInfo['cart_info']) ? json_decode($cartInfo['cart_info'], true) : $cartInfo['cart_info'];
|
||||
}
|
||||
unset($cartInfo);
|
||||
|
||||
if ($skuQtyMap && (int)($order['pid'] ?? 0) === -1) {
|
||||
$candidateOrders = [$order];
|
||||
$subOrders = $storeOrderServices->getColumn(['pid' => (int)$order['id'], 'is_system_del' => 0], 'id,uid,store_id,supplier_id,order_id,erp_order_id,total_num,pay_price,pay_postage,channel,pid');
|
||||
if ($subOrders && is_array($subOrders)) {
|
||||
$candidateOrders = array_merge($candidateOrders, $subOrders);
|
||||
}
|
||||
|
||||
$bestScore = -1;
|
||||
$bestCartInfos = $cartInfos;
|
||||
$bestLeftSkuQtyMap = $skuQtyMap;
|
||||
foreach ($candidateOrders as $cand) {
|
||||
if (!is_array($cand) || !isset($cand['id'])) continue;
|
||||
$candCartInfos = $storeOrderCartInfoServices->getCartColunm(['oid' => (int)$cand['id']], 'id,cart_id,cart_num,cart_info');
|
||||
foreach ($candCartInfos as &$candCartInfo) {
|
||||
$candCartInfo['cart_info'] = is_string($candCartInfo['cart_info']) ? json_decode($candCartInfo['cart_info'], true) : $candCartInfo['cart_info'];
|
||||
}
|
||||
unset($candCartInfo);
|
||||
|
||||
$need = $skuQtyMap;
|
||||
$score = 0;
|
||||
foreach ($candCartInfos as $candCart) {
|
||||
$ci = $candCart['cart_info'];
|
||||
if (!is_array($ci)) continue;
|
||||
$sku = (string)($ci['productInfo']['attrInfo']['code'] ?? $ci['productInfo']['attrInfo']['sku'] ?? $ci['productInfo']['sku'] ?? '');
|
||||
if ($sku === '' || !isset($need[$sku]) || (int)$need[$sku] <= 0) continue;
|
||||
$cartNum = (int)($candCart['cart_num'] ?? 0);
|
||||
$useNum = min($cartNum, (int)$need[$sku]);
|
||||
if ($useNum <= 0) continue;
|
||||
$score += $useNum;
|
||||
$need[$sku] -= $useNum;
|
||||
}
|
||||
|
||||
if ($score > $bestScore) {
|
||||
$bestScore = $score;
|
||||
$targetOrder = $cand;
|
||||
$bestCartInfos = $candCartInfos;
|
||||
$bestLeftSkuQtyMap = $need;
|
||||
}
|
||||
}
|
||||
$cartInfos = $bestCartInfos;
|
||||
$leftSkuQtyMap = $bestLeftSkuQtyMap;
|
||||
Log::info('jst.cb.trade_after_sale.split_match ' . json_encode([
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'best_score' => $bestScore,
|
||||
'target_store_order_id' => (int)($targetOrder['id'] ?? 0),
|
||||
'target_order_id' => (string)($targetOrder['order_id'] ?? ''),
|
||||
'target_erp_order_id' => (string)($targetOrder['erp_order_id'] ?? ''),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
$selectedCartInfo = [];
|
||||
if ($skuQtyMap) {
|
||||
foreach ($cartInfos as $cart) {
|
||||
$ci = $cart['cart_info'];
|
||||
if (!is_array($ci)) continue;
|
||||
$sku = (string)($ci['productInfo']['attrInfo']['code'] ?? $ci['productInfo']['attrInfo']['sku'] ?? $ci['productInfo']['sku'] ?? '');
|
||||
if ($sku === '' || !isset($skuQtyMap[$sku])) continue;
|
||||
$cartNum = (int)($cart['cart_num'] ?? 0);
|
||||
$applyNum = min($cartNum, (int)$skuQtyMap[$sku]);
|
||||
if ($applyNum <= 0) continue;
|
||||
$ci['id'] = $ci['id'] ?? (int)($cart['id'] ?? 0);
|
||||
$ci['cart_id'] = $ci['cart_id'] ?? (int)($cart['cart_id'] ?? 0);
|
||||
$ci['cart_num'] = $applyNum;
|
||||
$selectedCartInfo[] = $ci;
|
||||
$skuQtyMap[$sku] -= $applyNum;
|
||||
}
|
||||
}
|
||||
if (!$selectedCartInfo) {
|
||||
$selectedCartInfo = array_values(array_filter(array_column($cartInfos, 'cart_info'), 'is_array'));
|
||||
}
|
||||
|
||||
$refundNum = 0;
|
||||
$refundPrice = 0.00;
|
||||
foreach ($selectedCartInfo as $ci) {
|
||||
$refundNum += (int)($ci['cart_num'] ?? 0);
|
||||
if (isset($ci['truePrice'])) {
|
||||
$refundPrice = bcadd((string)$refundPrice, bcmul((string)$ci['truePrice'], (string)($ci['cart_num'] ?? 0), 4), 2);
|
||||
}
|
||||
}
|
||||
if ($refundPrice <= 0 && (int)($targetOrder['total_num'] ?? 0) > 0 && $refundNum > 0) {
|
||||
$refundPrice = (float)bcmul((string)$targetOrder['pay_price'], bcdiv((string)$refundNum, (string)$targetOrder['total_num'], 6), 2);
|
||||
}
|
||||
|
||||
$refundData = [
|
||||
'uid' => (int)$targetOrder['uid'],
|
||||
'store_id' => (int)$targetOrder['store_id'],
|
||||
'supplier_id' => (int)($targetOrder['supplier_id'] ?? 0),
|
||||
'store_order_id' => (int)$targetOrder['id'],
|
||||
'refund_num' => $refundNum > 0 ? $refundNum : (int)($targetOrder['total_num'] ?? 0),
|
||||
'apply_type' => $applyType,
|
||||
'refund_type' => $applyType,
|
||||
'refund_goods_type' => in_array($applyType, [2, 3]) ? $applyType : 1,
|
||||
'apply_price' => $refundPrice,
|
||||
'refund_price' => $refundPrice,
|
||||
'refund_reason' => $bizRemark !== '' ? $bizRemark : '聚水潭发起售后',
|
||||
'order_id' => $outerAsId,
|
||||
'add_time' => time(),
|
||||
'cart_info' => json_encode($selectedCartInfo, JSON_UNESCAPED_UNICODE),
|
||||
'channel' => (int)($targetOrder['channel'] ?? 0),
|
||||
];
|
||||
$res = $storeOrderRefundServices->save($refundData);
|
||||
$orderRefund = $storeOrderRefundServices->get((int)$res->id);
|
||||
Log::info('jst.cb.trade_after_sale.refund_created ' . json_encode([
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'store_order_id' => (int)$targetOrder['id'],
|
||||
'refund_id' => (int)$res->id,
|
||||
'apply_type' => $applyType,
|
||||
'refund_num' => $refundData['refund_num'],
|
||||
'refund_price' => $refundData['refund_price'],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
if ($shopStatus === 'CLOSED' || $bizStatus === 'CLOSED') {
|
||||
$storeOrderRefundServices->update((int)$orderRefund['id'], [
|
||||
'refund_type' => 3,
|
||||
'refuse_reason' => $bizRemark !== '' ? $bizRemark : '聚水潭关闭售后',
|
||||
'refunded_time' => time(),
|
||||
]);
|
||||
Log::info('jst.cb.trade_after_sale.refund_closed ' . json_encode([
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'refund_id' => (int)$orderRefund['id'],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return [];
|
||||
}
|
||||
|
||||
if (in_array($bizStatus, ['REFUNDED', 'REFUND_SUCCESS', 'FINISHED', 'COMPLETED'], true)) {
|
||||
/** @var OrderServices $orderServices */
|
||||
$orderServices = app()->make(OrderServices::class);
|
||||
$ok = (bool)$orderServices->receivedCallback(['outer_as_id' => $outerAsId, 'so_id' => $soId], 1, $soId !== '');
|
||||
Log::info('jst.cb.trade_after_sale.refund_finalize ' . json_encode([
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'refund_id' => (int)$orderRefund['id'],
|
||||
'success' => $ok ? 1 : 0,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
} else {
|
||||
Log::info('jst.cb.trade_after_sale.synced ' . json_encode([
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'refund_id' => (int)$orderRefund['id'],
|
||||
'apply_type' => (int)($orderRefund['apply_type'] ?? 0),
|
||||
'refund_type' => (int)($orderRefund['refund_type'] ?? 0),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.cb.trade_after_sale.error ' . json_encode([
|
||||
'outer_as_id' => $outerAsId,
|
||||
'so_id' => $soId,
|
||||
'action_name' => $actionName,
|
||||
'shop_status' => $shopStatus,
|
||||
'biz_status' => $bizStatus,
|
||||
'biz_type' => $bizType,
|
||||
'error' => [
|
||||
'class' => get_class($e),
|
||||
'message' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
344
app/haichao/sync/JstProductSync.php
Normal file
344
app/haichao/sync/JstProductSync.php
Normal file
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\sync;
|
||||
|
||||
use app\haichao\HaichaoJstServices;
|
||||
use app\haichao\util\CacheProduct;
|
||||
use app\haichao\util\ImageUtil;
|
||||
use app\jobs\product\ProductStockTips;
|
||||
use app\services\product\product\StoreDescriptionServices;
|
||||
use app\services\product\product\StoreProductServices;
|
||||
use app\services\product\sku\StoreProductAttrServices;
|
||||
use Exception;
|
||||
use think\facade\Log;
|
||||
use Throwable;
|
||||
|
||||
class JstProductSync extends HaichaoJstServices
|
||||
{
|
||||
public function syncImage(): void
|
||||
{
|
||||
/** @var StoreProductServices $make */
|
||||
$make = app()->make(StoreProductServices::class);
|
||||
$ps = $make->count(['is_del' => 0]);
|
||||
$iil = $this->getInneropenItemList();
|
||||
/** @var StoreDescriptionServices $proDesServices */
|
||||
$proDesServices = app()->make(StoreDescriptionServices::class);
|
||||
/** @var StoreProductServices $productServices */
|
||||
$productServices = app()->make(StoreProductServices::class);
|
||||
foreach ($iil as $item) {
|
||||
$itemSpuId = $item['item_spu_id'];
|
||||
try {
|
||||
$inneropenProduct = $this->getInneropenItemDetail($itemSpuId);
|
||||
} catch (Throwable $e) {
|
||||
$inneropenProduct = $this->getInneropenItemDetail($itemSpuId);
|
||||
}
|
||||
$images = "";
|
||||
foreach ($inneropenProduct['item_photo']['item_detail_images'] as $oImage) {
|
||||
$images .= "<p><img src=\"" . ImageUtil::urlToOssUrl($oImage) . "\"></p>";
|
||||
}
|
||||
$pid = $productServices->value(['code' => $item['style_code']], 'id');
|
||||
if ($pid) {
|
||||
$proDesServices->saveDescription($pid, $images);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function syncProductSingle($data, $pid = 0)
|
||||
{
|
||||
$productList = $data['datas'];
|
||||
/** @var StoreProductServices $productServices */
|
||||
$productServices = app()->make(StoreProductServices::class);
|
||||
/** @var StoreProductAttrServices $productAttrServices */
|
||||
$productAttrServices = app()->make(StoreProductAttrServices::class);
|
||||
try {
|
||||
$skuParList = [];
|
||||
$index = 1;
|
||||
$count = 0;
|
||||
$skuIds = '';
|
||||
foreach ($productList as $item) {
|
||||
$count++;
|
||||
$skuIds .= ($skuIds ? "," . $item['sku_id'] : $item['sku_id']);
|
||||
$skuParList[$index] = [
|
||||
'length' => $count,
|
||||
'code' => $skuIds
|
||||
];
|
||||
}
|
||||
$stockMap = [];
|
||||
foreach ($skuParList as $k => $skus) {
|
||||
$r = $this->findStockBySkuId($skus['code']);
|
||||
foreach ($r as $v) {
|
||||
if (!isset($stockMap[$v['sku_id']])) $stockMap[$v['sku_id']] = 0;
|
||||
$stockMap[$v['sku_id']] += $v['qty'];
|
||||
}
|
||||
}
|
||||
foreach ($productList as $item) {
|
||||
$pic = ($item['pic'] ? $item['pic'] : '');
|
||||
if (!$item['sale_price']) {
|
||||
$item['sale_price'] = 0;
|
||||
$item['s_price'] = 0;
|
||||
}
|
||||
if (!$item['market_price']) {
|
||||
$item['market_price'] = 0;
|
||||
}
|
||||
$item['c_price'] = $item['cost_price'];
|
||||
$item['i_id'] = $item['sku_id'];
|
||||
$supplierId = 0;
|
||||
try {
|
||||
if (isset($item['shop_id']) && $item['shop_id']) {
|
||||
/** @var \app\services\supplier\SystemSupplierServices $supplierServices */
|
||||
$supplierServices = app()->make(\app\services\supplier\SystemSupplierServices::class);
|
||||
$supplier = $supplierServices->getOne(['erp_shopid' => (int)$item['shop_id'], 'is_del' => 0], 'id');
|
||||
if ($supplier && isset($supplier['id'])) {
|
||||
$supplierId = (int)$supplier['id'];
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.product.sync.supplier_map_err ' . $e->getMessage());
|
||||
}
|
||||
$relationSupplierId = $pid ?: $supplierId;
|
||||
$productInfo = [
|
||||
'image' => $pic,
|
||||
'slider_image' => json_encode([$pic]),
|
||||
'store_name' => $item['name'],
|
||||
'store_info' => $item['name'],
|
||||
'cate_id' => 0,
|
||||
'price' => floatval($item['sale_price'] ?? 0),
|
||||
'ot_price' => floatval($item['market_price']),
|
||||
'delivery_type' => '1',
|
||||
'freight' => 1,
|
||||
'add_time' => time(),
|
||||
'cost' => floatval($item['c_price']),
|
||||
'ficti' => 0,
|
||||
'spec_type' => 1,
|
||||
'code' => $item['i_id'],
|
||||
'type' => $relationSupplierId ? 2 : 0,
|
||||
'relation_id' => $relationSupplierId,
|
||||
];
|
||||
$stockSum = 0;
|
||||
$detail = $details = $value = [];
|
||||
$items = $item;
|
||||
if ($items['properties_value']) {
|
||||
$detail[] = $items['properties_value'];
|
||||
$details[] = [
|
||||
'name' => $items['properties_value'],
|
||||
'select' => false
|
||||
];
|
||||
$stock = $stockMap[$items['sku_id']] ?? 0;
|
||||
$skuCost = floatval($items['cost_price']);
|
||||
$skuSettle = $relationSupplierId ? $skuCost : 0.0;
|
||||
$value[] = [
|
||||
'bar_code' => '',
|
||||
'brokerage' => 0,
|
||||
'brokerage_two' => 0,
|
||||
'code' => $items['sku_id'],
|
||||
'settle_price' => $skuSettle,
|
||||
'cost' => $skuCost,
|
||||
'detail' => ['规格' => $items['properties_value']],
|
||||
'ot_price' => floatval($items['market_price']),
|
||||
'pic' => ImageUtil::urlToOssUrl($items['pic']),
|
||||
'price' => floatval($items['sale_price']),
|
||||
'select' => true,
|
||||
'value1' => $items['properties_value'],
|
||||
'values' => $items['properties_value'],
|
||||
'vip_price' => 0,
|
||||
'volume' => 0,
|
||||
'weight' => 0,
|
||||
'stock' => $stock,
|
||||
'sum_stock' => $stock,
|
||||
];
|
||||
$stockSum = bcadd($stockSum, $stock, 0);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
$productInfo['stock'] = (int)$stockSum;
|
||||
$pidNow = $productServices->value(['code' => $item['i_id']], 'id');
|
||||
if (!isset($pidNow)) {
|
||||
$pidNow = $productServices->ErpProductSave($productInfo);
|
||||
} else {
|
||||
$productServices->update($pidNow, $productInfo);
|
||||
}
|
||||
ProductStockTips::dispatch([$pidNow, 0]);
|
||||
$attr = [[
|
||||
'value' => '规格',
|
||||
'detail' => $detail,
|
||||
'details' => $details,
|
||||
]];
|
||||
$skuList = $productAttrServices->validateProductAttr($attr, $value, $pidNow, 0, 0, 0);
|
||||
$productAttrServices->saveProductAttr($skuList, $pidNow);
|
||||
}
|
||||
$productServices->cacheTag()->clear();
|
||||
$productAttrServices->cacheTag()->clear();
|
||||
} catch (Exception $e) {
|
||||
Log::error('jst.product.sync.error ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function syncProduct(): void
|
||||
{
|
||||
/** @var StoreProductServices $make */
|
||||
$make = app()->make(StoreProductServices::class);
|
||||
$ps = $make->count(['is_del' => 0]);
|
||||
/** @var StoreProductServices $productServices */
|
||||
$productServices = app()->make(StoreProductServices::class);
|
||||
/** @var StoreProductAttrServices $productAttrServices */
|
||||
$productAttrServices = app()->make(StoreProductAttrServices::class);
|
||||
try {
|
||||
$pls = $this->getProductList();
|
||||
$productList = $pls['datas'];
|
||||
Log::info('jst.product.sync.start ' . count($productList));
|
||||
$skuParList = [];
|
||||
$index = 1;
|
||||
$count = 0;
|
||||
$skuIds = '';
|
||||
foreach ($productList as $item) {
|
||||
if (CacheProduct::isCache(md5($item['i_id']))) continue;
|
||||
foreach ($item['skus'] as $skus) {
|
||||
$count++;
|
||||
$skuIds .= $skus['sku_id'] . ",";
|
||||
$skuParList[$index] = [
|
||||
'length' => $count,
|
||||
'code' => $skuIds
|
||||
];
|
||||
if ($count >= 500) {
|
||||
$index++;
|
||||
$count = 0;
|
||||
$skuIds = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
$stockMap = [];
|
||||
foreach ($skuParList as $k => $skus) {
|
||||
$r = $this->findAllInneropenItemBySkuId($skus['code']);
|
||||
foreach ($r as $v) {
|
||||
$stockMap[$v['item_code']] = $v['stock'];
|
||||
}
|
||||
}
|
||||
foreach ($productList as $item) {
|
||||
if (!$item['skus']) continue;
|
||||
if (CacheProduct::isCache(md5($item['i_id']))) continue;
|
||||
$pic = ImageUtil::urlToOssUrl($item['pic']);
|
||||
$supplierId = 0;
|
||||
try {
|
||||
if (isset($item['shop_id']) && $item['shop_id']) {
|
||||
/** @var \app\services\supplier\SystemSupplierServices $supplierServices */
|
||||
$supplierServices = app()->make(\app\services\supplier\SystemSupplierServices::class);
|
||||
$supplier = $supplierServices->getOne(['erp_shopid' => (int)$item['shop_id'], 'is_del' => 0], 'id');
|
||||
if ($supplier && isset($supplier['id'])) {
|
||||
$supplierId = (int)$supplier['id'];
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
$salePrice = floatval($item['s_price'] ?? 0);
|
||||
if ($salePrice <= 0 && isset($item['skus']) && is_array($item['skus'])) {
|
||||
$skuSalePrices = [];
|
||||
foreach ($item['skus'] as $skuRow) {
|
||||
if (isset($skuRow['sale_price']) && is_numeric($skuRow['sale_price'])) {
|
||||
$skuSalePrices[] = floatval($skuRow['sale_price']);
|
||||
}
|
||||
}
|
||||
if ($skuSalePrices) {
|
||||
$salePrice = min($skuSalePrices);
|
||||
}
|
||||
}
|
||||
$marketPrice = floatval($item['market_price'] ?? 0);
|
||||
if ($marketPrice <= 0 && isset($item['skus']) && is_array($item['skus'])) {
|
||||
$skuMarketPrices = [];
|
||||
foreach ($item['skus'] as $skuRow) {
|
||||
if (isset($skuRow['market_price']) && is_numeric($skuRow['market_price'])) {
|
||||
$skuMarketPrices[] = floatval($skuRow['market_price']);
|
||||
}
|
||||
}
|
||||
if ($skuMarketPrices) {
|
||||
$marketPrice = max($skuMarketPrices);
|
||||
}
|
||||
}
|
||||
$costPrice = floatval($item['c_price'] ?? ($item['cost_price'] ?? 0));
|
||||
if ($costPrice <= 0 && isset($item['skus']) && is_array($item['skus'])) {
|
||||
$skuCostPrices = [];
|
||||
foreach ($item['skus'] as $skuRow) {
|
||||
if (isset($skuRow['cost_price']) && is_numeric($skuRow['cost_price'])) {
|
||||
$skuCostPrices[] = floatval($skuRow['cost_price']);
|
||||
}
|
||||
}
|
||||
if ($skuCostPrices) {
|
||||
$costPrice = max($skuCostPrices);
|
||||
}
|
||||
}
|
||||
$productInfo = [
|
||||
'image' => $pic,
|
||||
'slider_image' => json_encode([$pic]),
|
||||
'store_name' => $item['name'],
|
||||
'store_info' => $item['name'],
|
||||
'cate_id' => 0,
|
||||
'price' => $salePrice,
|
||||
'ot_price' => $marketPrice,
|
||||
'delivery_type' => '1',
|
||||
'freight' => 1,
|
||||
'add_time' => time(),
|
||||
'cost' => $costPrice,
|
||||
'ficti' => 0,
|
||||
'spec_type' => 1,
|
||||
'code' => $item['i_id'],
|
||||
'type' => $supplierId ? 2 : 0,
|
||||
'relation_id' => $supplierId,
|
||||
];
|
||||
$stockSum = 0;
|
||||
$detail = $details = $value = [];
|
||||
foreach ($item['skus'] as $items) {
|
||||
if (!$items['properties_value']) continue 2;
|
||||
$detail[] = $items['properties_value'];
|
||||
$details[] = [
|
||||
'name' => $items['properties_value'],
|
||||
'select' => false
|
||||
];
|
||||
$stock = $stockMap[$items['sku_id']] ?? 0;
|
||||
$skuCost = floatval($items['cost_price']);
|
||||
$skuSettle = $supplierId ? $skuCost : 0.0;
|
||||
$value[] = [
|
||||
'bar_code' => '',
|
||||
'brokerage' => 0,
|
||||
'brokerage_two' => 0,
|
||||
'code' => $items['sku_id'],
|
||||
'settle_price' => $skuSettle,
|
||||
'cost' => $skuCost,
|
||||
'detail' => ['规格' => $items['properties_value']],
|
||||
'ot_price' => floatval($items['market_price']),
|
||||
'pic' => ImageUtil::urlToOssUrl($items['pic']),
|
||||
'price' => floatval($items['sale_price']),
|
||||
'select' => true,
|
||||
'value1' => $items['properties_value'],
|
||||
'values' => $items['properties_value'],
|
||||
'vip_price' => 0,
|
||||
'volume' => 0,
|
||||
'weight' => 0,
|
||||
'stock' => $stock,
|
||||
'sum_stock' => $stock,
|
||||
];
|
||||
$stockSum = bcadd($stockSum, $stock, 0);
|
||||
}
|
||||
$productInfo['stock'] = (int)$stockSum;
|
||||
$pid = $productServices->value(['code' => $item['i_id']], 'id');
|
||||
if (!isset($pid)) {
|
||||
$pid = $productServices->ErpProductSave($productInfo);
|
||||
} else {
|
||||
$productServices->update($pid, $productInfo);
|
||||
}
|
||||
ProductStockTips::dispatch([$pid, 0]);
|
||||
$attr = [[
|
||||
'value' => '规格',
|
||||
'detail' => $detail,
|
||||
'details' => $details,
|
||||
]];
|
||||
$skuList = $productAttrServices->validateProductAttr($attr, $value, $pid, 0, 0, 0);
|
||||
$productAttrServices->saveProductAttr($skuList, $pid);
|
||||
CacheProduct::setCache(md5($item['i_id']), $item['i_id']);
|
||||
}
|
||||
$productServices->cacheTag()->clear();
|
||||
$productAttrServices->cacheTag()->clear();
|
||||
Log::info('jst.product.sync.end');
|
||||
} catch (Exception $e) {
|
||||
Log::error('jst.product.sync.error ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
115
app/haichao/sync/JstStorkSync.php
Normal file
115
app/haichao/sync/JstStorkSync.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\sync;
|
||||
|
||||
use app\haichao\HaichaoJstServices;
|
||||
use app\jobs\product\ProductSyncErp;
|
||||
use app\services\product\product\StoreProductServices;
|
||||
use app\services\product\sku\StoreProductAttrServices;
|
||||
use app\services\product\sku\StoreProductAttrValueServices;
|
||||
use app\services\store\SystemStoreServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use think\facade\Log;
|
||||
use Exception;
|
||||
|
||||
class JstStorkSync extends HaichaoJstServices
|
||||
{
|
||||
public function syncStorkFromInneropenErp(): void
|
||||
{
|
||||
/** @var StoreProductAttrValueServices $storeProductAttrValueServices */
|
||||
$storeProductAttrValueServices = app()->make(StoreProductAttrValueServices::class);
|
||||
Log::info('jst.stork.sync.start');
|
||||
$inventorySkuIds = [];
|
||||
$skuIds = '';
|
||||
$index = 1;
|
||||
$count = 0;
|
||||
foreach ($storeProductAttrValueServices->getList(['is_del' => 0]) as $item) {
|
||||
if ($item['code']) {
|
||||
$skuIds .= $item['code'] . ',';
|
||||
$count++;
|
||||
$inventorySkuIds[$index] = [
|
||||
'length' => $count,
|
||||
'ids' => $skuIds,
|
||||
];
|
||||
if ($count >= 500) {
|
||||
$skuIds = '';
|
||||
$index++;
|
||||
$count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
$productStock = [];
|
||||
foreach ($inventorySkuIds as $inventorySkuId) {
|
||||
foreach ($this->getInneropenStockAndDetailList($inventorySkuId['ids']) as $product) {
|
||||
$productStock[] = [
|
||||
'code' => $product['item_code'],
|
||||
'stock' => $product['stock']
|
||||
];
|
||||
}
|
||||
}
|
||||
Log::info('jst.stork.sync.merge ' . count($productStock));
|
||||
foreach ($productStock as $item) {
|
||||
$storeProductAttrValueServices->update(['code' => $item['code']], [
|
||||
'stock' => $item['stock'],
|
||||
'sum_stock' => $item['stock']
|
||||
]);
|
||||
}
|
||||
Log::info('jst.stork.sync.end');
|
||||
}
|
||||
|
||||
public function syncStockFromErp(array $ids): bool
|
||||
{
|
||||
try {
|
||||
/** @var StoreProductServices $storeProductServices */
|
||||
$storeProductServices = app()->make(StoreProductServices::class);
|
||||
/** @var StoreProductAttrValueServices $storeProductAttrValueServices */
|
||||
$storeProductAttrValueServices = app()->make(StoreProductAttrValueServices::class);
|
||||
$list = $storeProductAttrValueServices->getSkuArray(['product_id' => $ids, 'type' => 0], 'code', 'id');
|
||||
$values = array_filter(array_values($list));
|
||||
if (empty($values)) {
|
||||
throw new AdminException('没有符合同步库存的商品');
|
||||
}
|
||||
$skuData = $skuMap = [];
|
||||
$basic = 20;
|
||||
$num = count($values);
|
||||
$rate = ceil($num / $basic);
|
||||
for ($i = 0; $i < $rate; $i++) {
|
||||
$code = array_slice($values, $i * $basic, $basic);
|
||||
$codeStr = implode(',', $code);
|
||||
$result = $this->getStork($codeStr)['data'];
|
||||
if (!empty($result['inventorys'])) {
|
||||
foreach ($result['inventorys'] as $inventory) {
|
||||
$skuMap[$inventory['sku_id']] = $inventory['qty'] - $inventory['order_lock'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($skuMap)) {
|
||||
foreach ($skuMap as $key => $item) {
|
||||
if ($id = array_search($key, $list)) {
|
||||
$skuData[] = ['id' => $id, 'stock' => $item, 'sum_stock' => $item];
|
||||
}
|
||||
}
|
||||
}
|
||||
$storeProductServices->transaction(function () use ($ids, $skuData, $storeProductAttrValueServices, $storeProductServices) {
|
||||
$storeProductAttrValueServices->saveAll($skuData);
|
||||
$productData = $storeProductAttrValueServices->getProductStockByValues($ids);
|
||||
$storeProductServices->saveAll($productData);
|
||||
});
|
||||
/** @var SystemStoreServices $systemStoreServices */
|
||||
$systemStoreServices = app()->make(SystemStoreServices::class);
|
||||
$systemStoreList = $systemStoreServices->getErpStore([['erp_shop_id', '>', 0]]);
|
||||
if (!empty($systemStoreList)) {
|
||||
foreach ($systemStoreList as $store) {
|
||||
ProductSyncErp::dispatchDo('syncBranchProductStock', [$ids[0], $skuMap, $store['id']]);
|
||||
}
|
||||
}
|
||||
$storeProductServices->cacheTag()->clear();
|
||||
/** @var StoreProductAttrServices $attrService */
|
||||
$attrService = app()->make(StoreProductAttrServices::class);
|
||||
$attrService->cacheTag()->clear();
|
||||
} catch (Exception $e) {
|
||||
Log::error('jst.stork.sync.error ' . $e->getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
23
app/haichao/task/JstSyncStorkTask.php
Normal file
23
app/haichao/task/JstSyncStorkTask.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\task;
|
||||
|
||||
use app\haichao\HaichaoJstServices;
|
||||
use crmeb\interfaces\ListenerInterface;
|
||||
use crmeb\utils\Cron;
|
||||
use think\facade\Log;
|
||||
|
||||
class JstSyncStorkTask extends Cron implements ListenerInterface
|
||||
{
|
||||
public function handle($event): void
|
||||
{
|
||||
// 停用库存同步流程
|
||||
// return;
|
||||
|
||||
$this->tick(150000, function () {
|
||||
Log::info('jst.task.stork.tick');
|
||||
HaichaoJstServices::getServices()->getSyncStork()->syncStorkFromInneropenErp();
|
||||
Log::info('jst.task.stork.tick.done');
|
||||
});
|
||||
}
|
||||
}
|
||||
21
app/haichao/task/JstSyncTask.php
Normal file
21
app/haichao/task/JstSyncTask.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace app\haichao\task;
|
||||
|
||||
use app\haichao\HaichaoJstServices;
|
||||
use crmeb\interfaces\ListenerInterface;
|
||||
use crmeb\utils\Cron;
|
||||
use think\facade\Log;
|
||||
|
||||
class JstSyncTask extends Cron implements ListenerInterface
|
||||
{
|
||||
public function handle($event): void
|
||||
{
|
||||
return;
|
||||
$this->tick(20000, function () {
|
||||
Log::info('jst.task.product.tick');
|
||||
HaichaoJstServices::getServices()->getSyncProduct()->syncProduct();
|
||||
Log::info('jst.task.product.tick.done');
|
||||
});
|
||||
}
|
||||
}
|
||||
90
app/jobs/haichao/JstAfterSalePushJob.php
Normal file
90
app/jobs/haichao/JstAfterSalePushJob.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\jobs\haichao;
|
||||
|
||||
use app\haichao\HaichaoJstServices;
|
||||
use crmeb\basic\BaseJobs;
|
||||
use crmeb\traits\QueueTrait;
|
||||
use think\facade\Log;
|
||||
|
||||
class JstAfterSalePushJob extends BaseJobs
|
||||
{
|
||||
use QueueTrait;
|
||||
|
||||
public static function queueName()
|
||||
{
|
||||
return (string)env('QUEUE_LISTEN_NAME', 'CRMEB_PRO');
|
||||
}
|
||||
|
||||
public function create(int $refundId, int $times = 0): bool
|
||||
{
|
||||
Log::info('jst.after_sale.create.job_start ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'times' => $times
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
try {
|
||||
HaichaoJstServices::getServices()->afterSaleCreatePush($refundId);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.after_sale.create.job_error ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'times' => $times,
|
||||
'msg' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
if ($times < 3) {
|
||||
self::dispatchSece(10 * ($times + 1), 'create', [$refundId, $times + 1]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function cancel(int $refundId, int $times = 0): bool
|
||||
{
|
||||
Log::info('jst.after_sale.cancel.job_start ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'times' => $times
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
try {
|
||||
HaichaoJstServices::getServices()->afterSaleCancelPush($refundId);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.after_sale.cancel.job_error ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'times' => $times,
|
||||
'msg' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
if ($times < 3) {
|
||||
self::dispatchSece(10 * ($times + 1), 'cancel', [$refundId, $times + 1]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function refunded(int $refundId, int $times = 0): bool
|
||||
{
|
||||
Log::info('jst.after_sale.refunded.job_start ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'times' => $times
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
try {
|
||||
HaichaoJstServices::getServices()->afterSaleRefundedPush($refundId);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.after_sale.refunded.job_error ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'times' => $times,
|
||||
'msg' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
if ($times < 3) {
|
||||
self::dispatchSece(10 * ($times + 1), 'refunded', [$refundId, $times + 1]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,11 @@ namespace app\jobs\product;
|
||||
use app\services\product\product\StoreProductServices;
|
||||
use app\services\product\sku\StoreProductAttrServices;
|
||||
use app\services\product\sku\StoreProductAttrValueServices;
|
||||
use app\haichao\HaichaoJstServices;
|
||||
use crmeb\basic\BaseJobs;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\services\erp\Erp as erpServices;
|
||||
use crmeb\traits\QueueTrait;
|
||||
use think\facade\Log;
|
||||
|
||||
class ProductSyncErp extends BaseJobs
|
||||
{
|
||||
@@ -118,55 +119,164 @@ class ProductSyncErp extends BaseJobs
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function productFromErp($spuArr)
|
||||
public function productFromErp($spuArr, int $supplierId = 0)
|
||||
{
|
||||
try {
|
||||
$result = (new erpServices())->serviceDriver('product')->syncProduct([$spuArr]);
|
||||
$productList = $result['datas'];
|
||||
Log::info('admin.manual.jst.product.sync.request', ['spu' => $spuArr]);
|
||||
$result = app()->make(HaichaoJstServices::class)->getProductListKuan($spuArr);
|
||||
$resultPreview = null;
|
||||
try {
|
||||
$resultPreview = mb_substr(json_encode($result, JSON_UNESCAPED_UNICODE), 0, 2000);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
Log::info('admin.manual.jst.product.sync.api_result', [
|
||||
'spu' => $spuArr,
|
||||
'page_index' => $result['page_index'] ?? null,
|
||||
'page_count' => $result['page_count'] ?? null,
|
||||
'datas_count' => isset($result['datas']) && is_array($result['datas']) ? count($result['datas']) : 0,
|
||||
'result_preview' => $resultPreview,
|
||||
]);
|
||||
$productList = $result['datas'] ?? [];
|
||||
$productInfo = [];
|
||||
/** @var StoreProductServices $productServices */
|
||||
$productServices = app()->make(StoreProductServices::class);
|
||||
/** @var StoreProductAttrServices $productAttrServices */
|
||||
$productAttrServices = app()->make(StoreProductAttrServices::class);
|
||||
|
||||
Log::info('admin.manual.jst.product.sync.response', [
|
||||
'spu' => $spuArr,
|
||||
'datas_count' => is_array($productList) ? count($productList) : 0
|
||||
]);
|
||||
$relationSupplierId = $supplierId > 0 ? $supplierId : 0;
|
||||
foreach ($productList as $item) {
|
||||
Log::info('admin.manual.jst.product.sync.item', [
|
||||
'spu' => $spuArr,
|
||||
'i_id' => $item['i_id'] ?? '',
|
||||
'name' => $item['name'] ?? '',
|
||||
'sku_count' => isset($item['skus']) && is_array($item['skus']) ? count($item['skus']) : 0
|
||||
]);
|
||||
$skuRows = isset($item['skus']) && is_array($item['skus']) ? $item['skus'] : [];
|
||||
$salePrice = floatval($item['s_price'] ?? 0);
|
||||
if ($salePrice <= 0 && $skuRows) {
|
||||
$skuSalePrices = [];
|
||||
foreach ($skuRows as $skuRow) {
|
||||
if (isset($skuRow['sale_price']) && is_numeric($skuRow['sale_price'])) {
|
||||
$skuSalePrices[] = floatval($skuRow['sale_price']);
|
||||
}
|
||||
}
|
||||
if ($skuSalePrices) {
|
||||
$salePrice = min($skuSalePrices);
|
||||
}
|
||||
}
|
||||
$marketPrice = floatval($item['market_price'] ?? 0);
|
||||
if ($marketPrice <= 0 && $skuRows) {
|
||||
$skuMarketPrices = [];
|
||||
foreach ($skuRows as $skuRow) {
|
||||
if (isset($skuRow['market_price']) && is_numeric($skuRow['market_price'])) {
|
||||
$skuMarketPrices[] = floatval($skuRow['market_price']);
|
||||
}
|
||||
}
|
||||
if ($skuMarketPrices) {
|
||||
$marketPrice = max($skuMarketPrices);
|
||||
}
|
||||
}
|
||||
$costPrice = floatval($item['c_price'] ?? ($item['cost_price'] ?? 0));
|
||||
if ($costPrice <= 0 && $skuRows) {
|
||||
$skuCostPrices = [];
|
||||
foreach ($skuRows as $skuRow) {
|
||||
if (isset($skuRow['cost_price']) && is_numeric($skuRow['cost_price'])) {
|
||||
$skuCostPrices[] = floatval($skuRow['cost_price']);
|
||||
}
|
||||
}
|
||||
if ($skuCostPrices) {
|
||||
$costPrice = max($skuCostPrices);
|
||||
}
|
||||
}
|
||||
$pic = (string)($item['pic'] ?? ($skuRows[0]['pic'] ?? ''));
|
||||
$name = (string)($item['name'] ?? ($skuRows[0]['name'] ?? ''));
|
||||
$productInfo = [
|
||||
'image' => (string)$item['pic'],
|
||||
'slider_image' => json_encode([(string)$item['pic']]),
|
||||
'store_name' => $item['name'],
|
||||
'store_info' => $item['name'],
|
||||
'image' => $pic,
|
||||
'slider_image' => json_encode([$pic]),
|
||||
'store_name' => $name,
|
||||
'store_info' => $name,
|
||||
'cate_id' => 0,
|
||||
'price' => floatval($item['s_price']),
|
||||
'ot_price' => floatval($item['market_price']),
|
||||
'price' => $salePrice,
|
||||
'ot_price' => $marketPrice,
|
||||
'delivery_type' => '1,2,3',
|
||||
'freight' => 1,
|
||||
'is_show' => 0,
|
||||
'add_time' => time(),
|
||||
'cost' => floatval($item['c_price']),
|
||||
'cost' => $costPrice,
|
||||
'ficti' => 0,
|
||||
'spec_type' => 1,
|
||||
'code' => $item['i_id'],
|
||||
'type' => $relationSupplierId ? 2 : 0,
|
||||
'relation_id' => $relationSupplierId,
|
||||
];
|
||||
$detail = $details = $value = [];
|
||||
foreach ($item['skus'] as $items) {
|
||||
$detail[] = $items['properties_value'];
|
||||
$usedSpecValues = [];
|
||||
foreach ($skuRows as $skuIndex => $items) {
|
||||
$specValue = trim((string)($items['properties_value'] ?? ''));
|
||||
if ($specValue === '') {
|
||||
$specValue = trim((string)($items['name'] ?? ''));
|
||||
}
|
||||
if ($specValue === '') {
|
||||
$specValue = '规格' . ($skuIndex + 1);
|
||||
}
|
||||
if (isset($usedSpecValues[$specValue])) {
|
||||
$specValue = $specValue . '-' . ($skuIndex + 1);
|
||||
}
|
||||
$usedSpecValues[$specValue] = 1;
|
||||
$detail[] = $specValue;
|
||||
$details[] = [
|
||||
'name' => $items['properties_value'],
|
||||
'name' => $specValue,
|
||||
'select' => false
|
||||
];
|
||||
$skuCost = floatval($items['cost_price'] ?? $costPrice);
|
||||
$skuSettle = $relationSupplierId ? $skuCost : 0.0;
|
||||
$value[] = [
|
||||
'bar_code' => '',
|
||||
'brokerage' => 0,
|
||||
'brokerage_two' => 0,
|
||||
'code' => $items['sku_id'],
|
||||
'cost' => floatval($items['cost_price']),
|
||||
'detail' => ['规格' => $items['properties_value']],
|
||||
'ot_price' => floatval($items['market_price']),
|
||||
'code' => (string)($items['sku_id'] ?? ''),
|
||||
'settle_price' => $skuSettle,
|
||||
'cost' => $skuCost,
|
||||
'detail' => ['规格' => $specValue],
|
||||
'ot_price' => floatval($items['market_price'] ?? $marketPrice),
|
||||
'pic' => (string)$items['pic'],
|
||||
'price' => floatval($items['sale_price']),
|
||||
'price' => floatval($items['sale_price'] ?? $salePrice),
|
||||
'select' => true,
|
||||
'value1' => $items['properties_value'],
|
||||
'values' => $items['properties_value'],
|
||||
'value1' => $specValue,
|
||||
'values' => $specValue,
|
||||
'vip_price' => 0,
|
||||
'volume' => 0,
|
||||
'weight' => 0,
|
||||
'stock' => 0,
|
||||
];
|
||||
}
|
||||
if (!$value) {
|
||||
$specValue = '默认';
|
||||
$detail[] = $specValue;
|
||||
$details[] = [
|
||||
'name' => $specValue,
|
||||
'select' => false
|
||||
];
|
||||
$defCost = floatval($item['c_price'] ?? 0);
|
||||
$defSettle = $relationSupplierId ? $defCost : 0.0;
|
||||
$value[] = [
|
||||
'bar_code' => '',
|
||||
'brokerage' => 0,
|
||||
'brokerage_two' => 0,
|
||||
'code' => (string)($item['i_id'] ?? ''),
|
||||
'settle_price' => $defSettle,
|
||||
'cost' => $defCost,
|
||||
'detail' => ['规格' => $specValue],
|
||||
'ot_price' => floatval($item['market_price'] ?? 0),
|
||||
'pic' => (string)($item['pic'] ?? ''),
|
||||
'price' => floatval($item['s_price'] ?? 0),
|
||||
'select' => true,
|
||||
'value1' => $specValue,
|
||||
'values' => $specValue,
|
||||
'vip_price' => 0,
|
||||
'volume' => 0,
|
||||
'weight' => 0,
|
||||
@@ -174,6 +284,7 @@ class ProductSyncErp extends BaseJobs
|
||||
];
|
||||
}
|
||||
$pid = $productServices->value(['code' => $item['i_id']], 'id');
|
||||
$existed = !!$pid;
|
||||
if (!$pid) {
|
||||
$pid = $productServices->ErpProductSave($productInfo);
|
||||
}
|
||||
@@ -185,11 +296,18 @@ class ProductSyncErp extends BaseJobs
|
||||
|
||||
$skuList = $productAttrServices->validateProductAttr($attr, $value, $pid, 0, 0, 0);
|
||||
$productAttrServices->saveProductAttr($skuList, $pid);
|
||||
Log::info('admin.manual.jst.product.sync.save', [
|
||||
'spu' => $spuArr,
|
||||
'product_id' => $pid,
|
||||
'existed' => $existed,
|
||||
'sku_saved_count' => isset($skuList['value']) && is_array($skuList['value']) ? count($skuList['value']) : 0
|
||||
]);
|
||||
}
|
||||
|
||||
//清除数据缓存
|
||||
$productServices->cacheTag()->clear();
|
||||
$productAttrServices->cacheTag()->clear();
|
||||
Log::info('admin.manual.jst.product.sync.finish', ['spu' => $spuArr, 'count' => count($productList)]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -198,6 +316,7 @@ class ProductSyncErp extends BaseJobs
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine()
|
||||
]);
|
||||
Log::error('admin.manual.jst.product.sync.error', ['spu' => $spuArr, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
// +----------------------------------------------------------------------
|
||||
namespace app\listener\order;
|
||||
|
||||
use app\jobs\haichao\JstAfterSalePushJob;
|
||||
use app\jobs\notice\SocketPushJob;
|
||||
use crmeb\interfaces\ListenerInterface;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 订单申请退款事件
|
||||
@@ -29,6 +31,16 @@ class ApplyRefund implements ListenerInterface
|
||||
|
||||
SocketPushJob::dispatchDo('sendApplyRefund', [$order]);
|
||||
|
||||
try {
|
||||
$jobId = JstAfterSalePushJob::dispatchDo('create', [(int)$refundId]);
|
||||
Log::info('jst.after_sale.create.dispatch_ok ' . json_encode([
|
||||
'refund_id' => (int)$refundId,
|
||||
'job_id' => $jobId,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.after_sale.create.dispatch_error ' . $e->getMessage());
|
||||
}
|
||||
|
||||
//退款消息推送
|
||||
event('notice.notice', [['order' => $order], 'send_order_apply_refund']);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace app\listener\order;
|
||||
|
||||
|
||||
use app\haichao\HaichaoJstServices;
|
||||
use app\jobs\agent\AgentJob;
|
||||
use app\jobs\order\OrderCreateAfterJob;
|
||||
use app\jobs\order\OrderDeliveryJob;
|
||||
@@ -134,10 +135,16 @@ class Pay implements ListenerInterface
|
||||
if ($orderInfo['shipping_type'] == 2 || $orderInfo['product_type'] == 1) {
|
||||
event('order.routine.shipping', ['product', $orderInfo, 4, '', '']);
|
||||
}
|
||||
// 同步订单
|
||||
// if (sys_config('erp_open')) {
|
||||
// OrderSyncJob::dispatchDo('syncOrder', [(int)$orderInfo['id']]);
|
||||
// }
|
||||
try {
|
||||
$pushRet = HaichaoJstServices::getServices()->orderPush((int)$orderInfo['id'], 'WAIT_SELLER_SEND_GOODS');
|
||||
Log::info('jst.event.order_pay.push', [
|
||||
'order_id' => $orderInfo['order_id'] ?? '',
|
||||
'oid' => (int)$orderInfo['id'],
|
||||
'ret' => $pushRet
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.event.order_pay.push.err ' . $e->getMessage());
|
||||
}
|
||||
if ($orderInfo['channel'] == 1) {
|
||||
$ChannelMerchantServices = app()->make(ChannelMerchantServices::class);
|
||||
$channelInfo = $ChannelMerchantServices->get(['uid' => $orderInfo['uid'], 'is_del' => 0]);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// +----------------------------------------------------------------------
|
||||
namespace app\listener\order;
|
||||
|
||||
use app\jobs\haichao\JstAfterSalePushJob;
|
||||
use app\jobs\order\OrderStatusJob;
|
||||
use app\jobs\product\ProductLogJob;
|
||||
use app\jobs\supplier\SupplierFinanceJob;use app\jobs\system\CapitalFlowJob;
|
||||
@@ -18,6 +19,7 @@ use app\services\order\StoreOrderServices;
|
||||
use app\services\order\StoreOrderStatusServices;
|
||||
use app\services\user\UserServices;
|
||||
use crmeb\interfaces\ListenerInterface;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 订单退款事件
|
||||
@@ -31,7 +33,9 @@ class Refund implements ListenerInterface
|
||||
*/
|
||||
public function handle($event): void
|
||||
{
|
||||
[$data, $order] = $event;
|
||||
$data = $event[0] ?? [];
|
||||
$order = $event[1] ?? [];
|
||||
$refundId = (int)($event[3] ?? 0);
|
||||
$order = is_array($order) ? $order : $order->toArray();
|
||||
//修改开票数据退款状态
|
||||
$orderInvoiceServices = app()->make(StoreOrderInvoiceServices::class);
|
||||
@@ -76,5 +80,18 @@ class Refund implements ListenerInterface
|
||||
}
|
||||
//对外接口推送订单
|
||||
event('out.outPush', ['refund_create_push', ['order_id' => (int)$order['id']]]);
|
||||
|
||||
if ($refundId > 0) {
|
||||
try {
|
||||
$jobId = JstAfterSalePushJob::dispatchDo('refunded', [$refundId]);
|
||||
Log::info('jst.after_sale.refunded.dispatch_ok ' . json_encode([
|
||||
'refund_id' => $refundId,
|
||||
'order_id' => (int)$order['id'],
|
||||
'job_id' => $jobId,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('jst.after_sale.refunded.dispatch_error ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,6 +473,15 @@ class StoreOrderRefundServices extends BaseServices
|
||||
$orderInfo = $this->storeOrderServices->get((int)$orderRefundInfo['store_order_id']);
|
||||
//订单拒绝退款事件
|
||||
event('order.refuseRefund', [$orderInfo]);
|
||||
try {
|
||||
$jobId = \app\jobs\haichao\JstAfterSalePushJob::dispatchDo('cancel', [(int)$orderRefundInfo['id']]);
|
||||
\think\facade\Log::info('jst.after_sale.cancel.dispatch_ok ' . json_encode([
|
||||
'refund_id' => (int)$orderRefundInfo['id'],
|
||||
'job_id' => $jobId,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
} catch (\Throwable $e) {
|
||||
\think\facade\Log::error('jst.after_sale.cancel.dispatch_error ' . $e->getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -538,6 +547,15 @@ class StoreOrderRefundServices extends BaseServices
|
||||
$storeOrderCartInfoServices->clearOrderCartInfo($oid);
|
||||
// 推送订单
|
||||
event('out.outPush', ['refund_cancel_push', ['order_id' => (int)$orderRefundInfo['id']]]);
|
||||
try {
|
||||
$jobId = \app\jobs\haichao\JstAfterSalePushJob::dispatchDo('cancel', [(int)$orderRefundInfo['id']]);
|
||||
\think\facade\Log::info('jst.after_sale.cancel.dispatch_ok ' . json_encode([
|
||||
'refund_id' => (int)$orderRefundInfo['id'],
|
||||
'job_id' => $jobId,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
} catch (\Throwable $e) {
|
||||
\think\facade\Log::error('jst.after_sale.cancel.dispatch_error ' . $e->getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -710,7 +728,7 @@ class StoreOrderRefundServices extends BaseServices
|
||||
return $order;
|
||||
});
|
||||
//订单同意退款事件
|
||||
event('order.refund', [$refundData, $order, 'order_refund']);
|
||||
event('order.refund', [$refundData, $order, 'order_refund', (int)$id]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user