feat: add third-party order logistics APIs

This commit is contained in:
wushumin
2026-06-11 14:34:12 +08:00
parent d13db60618
commit fa267c4413
4 changed files with 496 additions and 30 deletions

View File

@@ -2,6 +2,7 @@
namespace app\controller\open;
use app\support\AppraisalServicePricePackageService;
use app\support\EnterpriseOpenApiAuthService;
use app\support\EnterpriseOrderService;
use support\Request;
@@ -36,6 +37,65 @@ class OrdersController
return api_success($result, !empty($result['idempotent']) ? '订单已存在' : '订单已创建');
}
public function shipping(Request $request)
{
try {
$auth = (new EnterpriseOpenApiAuthService())->authenticate($request);
} catch (\Throwable $e) {
return api_error($e->getMessage(), 401);
}
$payload = json_decode($request->rawBody(), true);
if (!is_array($payload)) {
return api_error('请求体必须是合法 JSON 对象', 422);
}
try {
$result = (new EnterpriseOrderService())->submitShipping($auth['customer'], $payload);
} catch (\InvalidArgumentException $e) {
return api_error($e->getMessage(), 422);
} catch (\RuntimeException $e) {
return api_error($e->getMessage(), 404);
} catch (\Throwable $e) {
return api_error('运单提交失败', 500, [
'detail' => $e->getMessage(),
]);
}
return api_success($result, '运单已提交');
}
public function servicePricePackages(Request $request)
{
try {
(new EnterpriseOpenApiAuthService())->authenticate($request);
} catch (\Throwable $e) {
return api_error($e->getMessage(), 401);
}
try {
$service = new AppraisalServicePricePackageService();
$serviceProvider = trim((string)$request->input('service_provider', ''));
$allowedProviders = array_column($service->providerOptions(), 'service_provider');
if ($serviceProvider !== '' && !in_array($serviceProvider, $allowedProviders, true)) {
return api_error('service_provider 无效', 422);
}
$providers = array_values(array_filter(
$service->serviceOptions(),
fn (array $item) => $serviceProvider === '' || (string)$item['service_provider'] === $serviceProvider
));
return api_success([
'service_providers' => array_map(fn (array $item) => $this->formatOpenServiceProvider($item), $providers),
]);
} catch (\Throwable $e) {
return api_error('套餐获取失败', 500, [
'detail' => $e->getMessage(),
]);
}
}
public function detail(Request $request)
{
try {
@@ -66,4 +126,41 @@ class OrdersController
'order' => $order,
]);
}
private function formatOpenServiceProvider(array $item): array
{
$packages = array_map(fn (array $package) => $this->formatOpenPricePackage($package), (array)($item['packages'] ?? []));
$defaultPackageCode = '';
foreach ($packages as $package) {
if (!empty($package['is_default'])) {
$defaultPackageCode = (string)$package['price_package_code'];
break;
}
}
if ($defaultPackageCode === '' && isset($packages[0])) {
$defaultPackageCode = (string)$packages[0]['price_package_code'];
}
return [
'service_provider' => (string)$item['service_provider'],
'service_provider_text' => (string)$item['service_provider_text'],
'sla_hours' => (int)$item['sla_hours'],
'default_price_package_code' => $defaultPackageCode,
'packages' => $packages,
];
}
private function formatOpenPricePackage(array $package): array
{
return [
'service_provider' => (string)$package['service_provider'],
'service_provider_text' => (string)$package['service_provider_text'],
'price_package_name' => (string)$package['package_name'],
'price_package_code' => (string)$package['package_code'],
'price_package_price' => (float)$package['price'],
'description' => (string)$package['description'],
'is_default' => (bool)$package['is_default'],
'sla_hours' => (int)$package['sla_hours'],
];
}
}

View File

@@ -169,6 +169,136 @@ class EnterpriseOrderService
return $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']);
}
public function submitShipping(array $customer, array $payload): array
{
$externalOrderNo = trim((string)($payload['external_order_no'] ?? ''));
$expressCompany = trim((string)($payload['express_company'] ?? ''));
$trackingNo = trim((string)($payload['tracking_no'] ?? ''));
if ($externalOrderNo === '') {
throw new \InvalidArgumentException('external_order_no 不能为空');
}
if ($expressCompany === '' || $trackingNo === '') {
throw new \InvalidArgumentException('快递公司和运单号不能为空');
}
$ref = Db::name('enterprise_customer_order_refs')
->where('customer_id', (int)$customer['id'])
->where('external_order_no', $externalOrderNo)
->find();
if (!$ref) {
throw new \RuntimeException('订单不存在');
}
$order = Db::name('orders')->where('id', (int)$ref['order_id'])->find();
if (!$order) {
throw new \RuntimeException('订单不存在');
}
if ((string)$order['order_status'] !== 'pending_shipping') {
throw new \InvalidArgumentException('当前订单状态不支持提交运单');
}
$existing = Db::name('order_logistics')
->where('order_id', (int)$order['id'])
->where('logistics_type', 'send_to_center')
->order('id', 'desc')
->find();
$sameLogistics = $existing
&& (string)$existing['express_company'] === $expressCompany
&& (string)$existing['tracking_no'] === $trackingNo;
if ($sameLogistics) {
return [
'idempotent' => true,
'updated' => false,
'logistics' => $this->formatLogistics($existing),
'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']),
];
}
$now = date('Y-m-d H:i:s');
$latestDesc = sprintf('客户已提交寄送运单:%s %s等待鉴定中心签收。', $expressCompany, $trackingNo);
$updated = (bool)$existing;
$logisticsId = 0;
$resetLogisticsSync = false;
Db::startTrans();
try {
if ($existing) {
$logisticsId = (int)$existing['id'];
$resetLogisticsSync = true;
Db::name('order_logistics')->where('id', $logisticsId)->update([
'logistics_type' => 'send_to_center',
'express_company' => $expressCompany,
'tracking_no' => $trackingNo,
'tracking_status' => 'submitted',
'latest_desc' => $latestDesc,
'latest_time' => $now,
'updated_at' => $now,
]);
$nodeText = '已更新运单';
$nodeDesc = sprintf('客户更新了寄送运单:%s %s', $expressCompany, $trackingNo);
} else {
$logisticsId = (int)Db::name('order_logistics')->insertGetId([
'order_id' => (int)$order['id'],
'logistics_type' => 'send_to_center',
'express_company' => $expressCompany,
'tracking_no' => $trackingNo,
'tracking_status' => 'submitted',
'latest_desc' => $latestDesc,
'latest_time' => $now,
'created_at' => $now,
'updated_at' => $now,
]);
$nodeText = '已提交运单';
$nodeDesc = sprintf('客户已提交寄送运单:%s %s', $expressCompany, $trackingNo);
}
Db::name('order_logistics_nodes')->insert([
'logistics_id' => $logisticsId,
'node_time' => $now,
'node_desc' => $latestDesc,
'node_location' => '第三方',
'created_at' => $now,
]);
Db::name('orders')->where('id', (int)$order['id'])->update([
'display_status' => '已提交运单',
'updated_at' => $now,
]);
Db::name('order_timelines')->insert([
'order_id' => (int)$order['id'],
'node_code' => 'tracking_submitted',
'node_text' => $nodeText,
'node_desc' => $nodeDesc,
'operator_type' => 'system',
'operator_id' => null,
'occurred_at' => $now,
'created_at' => $now,
]);
Db::commit();
} catch (\Throwable $e) {
Db::rollback();
throw $e;
}
$syncService = new OrderLogisticsSyncService();
if ($resetLogisticsSync) {
Db::name('order_logistics_syncs')->where('logistics_id', $logisticsId)->delete();
}
$syncService->subscribeAsync($logisticsId);
$logistics = Db::name('order_logistics')->where('id', $logisticsId)->find();
return [
'idempotent' => false,
'updated' => $updated,
'logistics' => $this->formatLogistics($logistics),
'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']),
];
}
public function buildOrderProgress(int $customerId, array $ref, string $customerCode = ''): array
{
$order = Db::name('orders')->where('id', (int)$ref['order_id'])->find();