698 lines
29 KiB
PHP
698 lines
29 KiB
PHP
<?php
|
||
|
||
namespace app\support;
|
||
|
||
use support\Request;
|
||
use support\think\Db;
|
||
|
||
class EnterpriseOrderService
|
||
{
|
||
public function createOrder(array $customer, array $payload, Request $request): array
|
||
{
|
||
$externalOrderNo = trim((string)($payload['external_order_no'] ?? ''));
|
||
if ($externalOrderNo === '') {
|
||
throw new \InvalidArgumentException('external_order_no 不能为空');
|
||
}
|
||
|
||
$payloadHash = hash('sha256', json_encode($this->normalizePayloadForHash($payload), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||
$existingRef = Db::name('enterprise_customer_order_refs')
|
||
->where('customer_id', (int)$customer['id'])
|
||
->where('external_order_no', $externalOrderNo)
|
||
->find();
|
||
if ($existingRef) {
|
||
if (($existingRef['payload_hash'] ?? '') !== $payloadHash) {
|
||
throw new \RuntimeException('external_order_no 已存在,但请求内容不一致');
|
||
}
|
||
return [
|
||
'idempotent' => true,
|
||
'order' => $this->buildOrderProgress((int)$customer['id'], $existingRef, (string)$customer['customer_code']),
|
||
];
|
||
}
|
||
|
||
$serviceProvider = trim((string)($payload['service_provider'] ?? 'anxinyan'));
|
||
if (!in_array($serviceProvider, ['anxinyan', 'zhongjian'], true)) {
|
||
throw new \InvalidArgumentException('service_provider 无效');
|
||
}
|
||
|
||
$pricePackageCode = trim((string)($payload['price_package_code'] ?? ''));
|
||
try {
|
||
$servicePackage = $this->pricePackageSnapshot($serviceProvider, $pricePackageCode);
|
||
} catch (\RuntimeException $e) {
|
||
throw new \InvalidArgumentException($e->getMessage());
|
||
}
|
||
|
||
$product = $this->normalizeProduct((array)($payload['product_info'] ?? []));
|
||
$returnAddress = $this->normalizeReturnAddress((array)($payload['return_address'] ?? []));
|
||
$materials = $this->normalizeMaterials((array)($payload['materials'] ?? []));
|
||
|
||
$now = date('Y-m-d H:i:s');
|
||
$orderNo = 'AXY' . date('YmdHis') . mt_rand(100, 999);
|
||
$appraisalNo = 'AXY-APP-' . date('Ymd') . '-' . mt_rand(1000, 9999);
|
||
$estimated = date('Y-m-d H:i:s', strtotime(sprintf('+%d hours', (int)$servicePackage['sla_hours'])));
|
||
$userId = (new EnterpriseCustomerService())->ensureVirtualUser($customer);
|
||
$productName = $this->resolveProductName($product);
|
||
|
||
Db::startTrans();
|
||
try {
|
||
$orderId = (int)Db::name('orders')->insertGetId([
|
||
'order_no' => $orderNo,
|
||
'appraisal_no' => $appraisalNo,
|
||
'user_id' => $userId,
|
||
'service_mode' => 'physical',
|
||
'service_provider' => $serviceProvider,
|
||
'payment_status' => 'paid',
|
||
'order_status' => 'pending_shipping',
|
||
'display_status' => '待寄送商品',
|
||
'estimated_finish_time' => $estimated,
|
||
'source_channel' => 'enterprise_push',
|
||
'source_customer_id' => $customer['customer_code'],
|
||
'price_package_id' => $servicePackage['price_package_id'],
|
||
'price_package_name' => $servicePackage['price_package_name'],
|
||
'price_package_code' => $servicePackage['price_package_code'],
|
||
'price_package_price' => $servicePackage['price_package_price'],
|
||
'pay_amount' => $servicePackage['pay_amount'],
|
||
'paid_at' => $now,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
Db::name('order_products')->insert(array_merge($product, [
|
||
'order_id' => $orderId,
|
||
'product_name' => $productName,
|
||
'product_cover' => $materials[0]['file_url'] ?? '',
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]));
|
||
|
||
$extra = (array)($payload['extra_info'] ?? []);
|
||
Db::name('order_extras')->insert([
|
||
'order_id' => $orderId,
|
||
'purchase_channel' => trim((string)($extra['purchase_channel'] ?? '')),
|
||
'purchase_price' => (float)($extra['purchase_price'] ?? 0),
|
||
'purchase_date' => $extra['purchase_date'] ?? null,
|
||
'usage_status' => trim((string)($extra['usage_status'] ?? '')),
|
||
'condition_desc' => trim((string)($extra['condition_desc'] ?? '')),
|
||
'has_accessories' => !empty($extra['has_accessories']) ? 1 : 0,
|
||
'accessories_json' => json_encode(array_values((array)($extra['accessories'] ?? [])), JSON_UNESCAPED_UNICODE),
|
||
'remark' => trim((string)($extra['remark'] ?? '')),
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
if ($returnAddress) {
|
||
Db::name('order_return_addresses')->insert(array_merge($returnAddress, [
|
||
'order_id' => $orderId,
|
||
'user_address_id' => null,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]));
|
||
}
|
||
|
||
$shippingTarget = (new WarehouseService())->bindOrderTarget(
|
||
$orderId,
|
||
$serviceProvider,
|
||
!empty($product['category_id']) ? (int)$product['category_id'] : null,
|
||
null
|
||
);
|
||
|
||
$this->insertMaterials($orderId, $materials, $now);
|
||
$this->insertTimelines($orderId, $now, $shippingTarget);
|
||
$this->insertTask($orderId, $serviceProvider, $estimated, $now);
|
||
$this->insertInboundLogistics($orderId, $this->normalizeInboundLogistics($payload), $now);
|
||
|
||
Db::name('enterprise_customer_order_refs')->insert([
|
||
'customer_id' => (int)$customer['id'],
|
||
'external_order_no' => $externalOrderNo,
|
||
'order_id' => $orderId,
|
||
'order_no' => $orderNo,
|
||
'appraisal_no' => $appraisalNo,
|
||
'payload_hash' => $payloadHash,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
Db::commit();
|
||
} catch (\Throwable $e) {
|
||
Db::rollback();
|
||
throw $e;
|
||
}
|
||
|
||
(new EnterpriseWebhookService())->recordOrderEvent($orderId, 'order_created', [
|
||
'product_name' => $productName,
|
||
'price_package_name' => $servicePackage['price_package_name'],
|
||
'pay_amount' => (float)$servicePackage['pay_amount'],
|
||
]);
|
||
|
||
$ref = Db::name('enterprise_customer_order_refs')->where('order_id', $orderId)->find();
|
||
return [
|
||
'idempotent' => false,
|
||
'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']),
|
||
];
|
||
}
|
||
|
||
public function findOrder(array $customer, string $externalOrderNo = '', string $orderNo = ''): array
|
||
{
|
||
$query = Db::name('enterprise_customer_order_refs')->where('customer_id', (int)$customer['id']);
|
||
if ($externalOrderNo !== '') {
|
||
$query->where('external_order_no', $externalOrderNo);
|
||
} elseif ($orderNo !== '') {
|
||
$query->where('order_no', $orderNo);
|
||
} else {
|
||
throw new \InvalidArgumentException('external_order_no 或 order_no 不能为空');
|
||
}
|
||
|
||
$ref = $query->find();
|
||
if (!$ref) {
|
||
throw new \RuntimeException('订单不存在');
|
||
}
|
||
|
||
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 saveReturnAddress(array $customer, array $payload): array
|
||
{
|
||
$externalOrderNo = trim((string)($payload['external_order_no'] ?? ''));
|
||
if ($externalOrderNo === '') {
|
||
throw new \InvalidArgumentException('external_order_no 不能为空');
|
||
}
|
||
|
||
$returnAddress = $this->normalizeReturnAddress((array)($payload['return_address'] ?? []));
|
||
if (!$returnAddress) {
|
||
throw new \InvalidArgumentException('return_address 不能为空');
|
||
}
|
||
|
||
$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('订单不存在');
|
||
}
|
||
|
||
$returnLogistics = Db::name('order_logistics')
|
||
->where('order_id', (int)$order['id'])
|
||
->where('logistics_type', 'return_to_user')
|
||
->order('id', 'desc')
|
||
->find();
|
||
if (!empty($returnLogistics['tracking_no'])) {
|
||
throw new \InvalidArgumentException('回寄运单已生成,当前不可再修改寄回地址');
|
||
}
|
||
|
||
$existing = Db::name('order_return_addresses')->where('order_id', (int)$order['id'])->find();
|
||
$now = date('Y-m-d H:i:s');
|
||
$updated = (bool)$existing;
|
||
$snapshot = array_merge($returnAddress, [
|
||
'user_address_id' => null,
|
||
]);
|
||
|
||
Db::startTrans();
|
||
try {
|
||
if ($existing) {
|
||
Db::name('order_return_addresses')->where('order_id', (int)$order['id'])->update(array_merge($snapshot, [
|
||
'updated_at' => $now,
|
||
]));
|
||
$nodeText = '已更新寄回地址';
|
||
} else {
|
||
Db::name('order_return_addresses')->insert(array_merge($snapshot, [
|
||
'order_id' => (int)$order['id'],
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]));
|
||
$nodeText = '已确认寄回地址';
|
||
}
|
||
|
||
Db::name('order_timelines')->insert([
|
||
'order_id' => (int)$order['id'],
|
||
'node_code' => 'return_address_selected',
|
||
'node_text' => $nodeText,
|
||
'node_desc' => sprintf(
|
||
'大客户已确认寄回地址:%s%s%s%s',
|
||
$returnAddress['province'],
|
||
$returnAddress['city'],
|
||
$returnAddress['district'],
|
||
$returnAddress['detail_address']
|
||
),
|
||
'operator_type' => 'system',
|
||
'operator_id' => null,
|
||
'occurred_at' => $now,
|
||
'created_at' => $now,
|
||
]);
|
||
|
||
Db::commit();
|
||
} catch (\Throwable $e) {
|
||
Db::rollback();
|
||
throw $e;
|
||
}
|
||
|
||
return [
|
||
'updated' => $updated,
|
||
'return_address' => $this->formatReturnAddress($snapshot),
|
||
'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();
|
||
if (!$order) {
|
||
throw new \RuntimeException('订单不存在');
|
||
}
|
||
|
||
$timeline = Db::name('order_timelines')->where('order_id', (int)$order['id'])->order('occurred_at', 'asc')->select()->toArray();
|
||
$sendLogistics = Db::name('order_logistics')->where('order_id', (int)$order['id'])->where('logistics_type', 'send_to_center')->order('id', 'desc')->find();
|
||
$returnLogistics = Db::name('order_logistics')->where('order_id', (int)$order['id'])->where('logistics_type', 'return_to_user')->order('id', 'desc')->find();
|
||
$returnAddress = Db::name('order_return_addresses')->where('order_id', (int)$order['id'])->find();
|
||
$report = Db::name('reports')
|
||
->where('order_id', (int)$order['id'])
|
||
->where('report_status', 'published')
|
||
->order('id', 'desc')
|
||
->find();
|
||
$verify = $report ? (Db::name('report_verifies')->where('report_id', (int)$report['id'])->find() ?: null) : null;
|
||
|
||
return [
|
||
'customer_id' => $customerCode !== '' ? $customerCode : (string)$customerId,
|
||
'customer_code' => $customerCode !== '' ? $customerCode : (string)$customerId,
|
||
'external_order_no' => (string)$ref['external_order_no'],
|
||
'order_id' => (int)$order['id'],
|
||
'order_no' => (string)$order['order_no'],
|
||
'appraisal_no' => (string)$order['appraisal_no'],
|
||
'order_status' => (string)$order['order_status'],
|
||
'display_status' => (string)$order['display_status'],
|
||
'payment_status' => (string)$order['payment_status'],
|
||
'price_package_name' => (string)($order['price_package_name'] ?? ''),
|
||
'price_package_code' => (string)($order['price_package_code'] ?? ''),
|
||
'price_package_price' => (float)($order['price_package_price'] ?? 0),
|
||
'pay_amount' => (float)$order['pay_amount'],
|
||
'estimated_finish_time' => (string)($order['estimated_finish_time'] ?? ''),
|
||
'created_at' => (string)$order['created_at'],
|
||
'timeline' => array_map(fn(array $item) => [
|
||
'node_code' => (string)$item['node_code'],
|
||
'node_text' => (string)$item['node_text'],
|
||
'node_desc' => (string)$item['node_desc'],
|
||
'occurred_at' => (string)$item['occurred_at'],
|
||
], $timeline),
|
||
'inbound_logistics' => $this->formatLogistics($sendLogistics),
|
||
'return_address' => $returnAddress ? $this->formatReturnAddress($returnAddress) : null,
|
||
'return_logistics' => $this->formatLogistics($returnLogistics),
|
||
'report_summary' => $report ? [
|
||
'report_no' => (string)$report['report_no'],
|
||
'report_title' => (string)$report['report_title'],
|
||
'report_status' => (string)$report['report_status'],
|
||
'publish_time' => (string)($report['publish_time'] ?? ''),
|
||
'verify_url' => (string)($verify['verify_url'] ?? ''),
|
||
'report_page_url' => (string)($verify['verify_qrcode_url'] ?? ''),
|
||
'verify_status' => (string)($verify['verify_status'] ?? ''),
|
||
] : null,
|
||
];
|
||
}
|
||
|
||
private function normalizePayloadForHash(array $payload): array
|
||
{
|
||
ksort($payload);
|
||
foreach ($payload as &$value) {
|
||
if (is_array($value)) {
|
||
$value = $this->normalizePayloadForHash($value);
|
||
}
|
||
}
|
||
unset($value);
|
||
return $payload;
|
||
}
|
||
|
||
private function normalizeProduct(array $product): array
|
||
{
|
||
$productName = trim((string)($product['product_name'] ?? ''));
|
||
|
||
return [
|
||
'category_id' => !empty($product['category_id']) ? (int)$product['category_id'] : null,
|
||
'category_name' => trim((string)($product['category_name'] ?? '')),
|
||
'brand_id' => !empty($product['brand_id']) ? (int)$product['brand_id'] : null,
|
||
'brand_name' => trim((string)($product['brand_name'] ?? '')),
|
||
'color' => trim((string)($product['color'] ?? '')),
|
||
'size_spec' => trim((string)($product['size_spec'] ?? '')),
|
||
'serial_no' => trim((string)($product['serial_no'] ?? '')),
|
||
'product_name' => $productName,
|
||
];
|
||
}
|
||
|
||
private function normalizeReturnAddress(array $address): ?array
|
||
{
|
||
$requiredKeys = ['consignee', 'mobile', 'province', 'city', 'district', 'detail_address'];
|
||
$hasAnyValue = false;
|
||
foreach ($requiredKeys as $key) {
|
||
if (trim((string)($address[$key] ?? '')) !== '') {
|
||
$hasAnyValue = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!$hasAnyValue) {
|
||
return null;
|
||
}
|
||
|
||
foreach ($requiredKeys as $key) {
|
||
if (trim((string)($address[$key] ?? '')) === '') {
|
||
throw new \InvalidArgumentException("return_address.{$key} 不能为空");
|
||
}
|
||
}
|
||
|
||
return [
|
||
'consignee' => trim((string)$address['consignee']),
|
||
'mobile' => trim((string)$address['mobile']),
|
||
'province' => trim((string)$address['province']),
|
||
'city' => trim((string)$address['city']),
|
||
'district' => trim((string)$address['district']),
|
||
'detail_address' => trim((string)$address['detail_address']),
|
||
];
|
||
}
|
||
|
||
private function normalizeMaterials(array $materials): array
|
||
{
|
||
$list = [];
|
||
foreach ($materials as $index => $item) {
|
||
if (is_string($item)) {
|
||
$url = trim($item);
|
||
$itemCode = 'material_' . ($index + 1);
|
||
$itemName = '鉴定资料';
|
||
} elseif (is_array($item)) {
|
||
$url = trim((string)($item['file_url'] ?? $item['url'] ?? ''));
|
||
$itemCode = trim((string)($item['item_code'] ?? 'material_' . ($index + 1)));
|
||
$itemName = trim((string)($item['item_name'] ?? '鉴定资料'));
|
||
} else {
|
||
$url = '';
|
||
$itemCode = 'material_' . ($index + 1);
|
||
$itemName = '鉴定资料';
|
||
}
|
||
if ($url === '' || !preg_match('/^https?:\/\//i', $url)) {
|
||
throw new \InvalidArgumentException('materials 只支持 http/https 图片 URL');
|
||
}
|
||
$list[] = [
|
||
'item_code' => $itemCode,
|
||
'item_name' => $itemName,
|
||
'file_url' => $url,
|
||
'thumbnail_url' => is_array($item) ? trim((string)($item['thumbnail_url'] ?? $url)) : $url,
|
||
'is_required' => is_array($item) && !empty($item['is_required']) ? 1 : 0,
|
||
];
|
||
}
|
||
|
||
return $list;
|
||
}
|
||
|
||
private function normalizeInboundLogistics(array $payload): array
|
||
{
|
||
$logistics = (array)($payload['inbound_logistics'] ?? []);
|
||
if (!empty($payload['express_company']) || !empty($payload['tracking_no'])) {
|
||
$logistics = array_merge($logistics, [
|
||
'express_company' => $payload['express_company'] ?? ($logistics['express_company'] ?? ''),
|
||
'tracking_no' => $payload['tracking_no'] ?? ($logistics['tracking_no'] ?? ''),
|
||
]);
|
||
}
|
||
|
||
return $logistics;
|
||
}
|
||
|
||
private function resolveProductName(array $product): string
|
||
{
|
||
$productName = trim((string)($product['product_name'] ?? ''));
|
||
if ($productName !== '') {
|
||
return $productName;
|
||
}
|
||
return trim(($product['brand_name'] ?? '') . ' ' . ($product['category_name'] ?? ''));
|
||
}
|
||
|
||
private function pricePackageSnapshot(string $serviceProvider, string $packageCode = ''): array
|
||
{
|
||
return (new AppraisalServicePricePackageService())->snapshotForOrder($serviceProvider, 0, $packageCode);
|
||
}
|
||
|
||
private function insertMaterials(int $orderId, array $materials, string $now): void
|
||
{
|
||
foreach ($materials as $item) {
|
||
$uploadItemId = (int)Db::name('order_upload_items')->insertGetId([
|
||
'order_id' => $orderId,
|
||
'template_id' => null,
|
||
'item_code' => $item['item_code'],
|
||
'item_name' => $item['item_name'],
|
||
'is_required' => $item['is_required'],
|
||
'source_type' => 'initial',
|
||
'status' => 'uploaded',
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
Db::name('order_upload_files')->insert([
|
||
'order_upload_item_id' => $uploadItemId,
|
||
'file_id' => md5($item['file_url']),
|
||
'file_url' => $item['file_url'],
|
||
'thumbnail_url' => $item['thumbnail_url'],
|
||
'quality_status' => 'uploaded',
|
||
'quality_message' => '',
|
||
'uploaded_by_user_id' => null,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
}
|
||
}
|
||
|
||
private function insertTimelines(int $orderId, string $now, array $shippingTarget): void
|
||
{
|
||
Db::name('order_timelines')->insertAll([
|
||
[
|
||
'order_id' => $orderId,
|
||
'node_code' => 'created',
|
||
'node_text' => '下单成功',
|
||
'node_desc' => '大客户订单已推送并创建成功',
|
||
'operator_type' => 'system',
|
||
'operator_id' => null,
|
||
'occurred_at' => $now,
|
||
'created_at' => $now,
|
||
],
|
||
[
|
||
'order_id' => $orderId,
|
||
'node_code' => 'pending_shipping',
|
||
'node_text' => '待寄送商品',
|
||
'node_desc' => sprintf('请将商品寄送至%s', $shippingTarget['warehouse_name'] ?: '鉴定中心'),
|
||
'operator_type' => 'system',
|
||
'operator_id' => null,
|
||
'occurred_at' => $now,
|
||
'created_at' => $now,
|
||
],
|
||
]);
|
||
}
|
||
|
||
private function insertTask(int $orderId, string $serviceProvider, string $estimated, string $now): void
|
||
{
|
||
Db::name('appraisal_tasks')->insert([
|
||
'order_id' => $orderId,
|
||
'task_stage' => 'first_review',
|
||
'service_provider' => $serviceProvider,
|
||
'status' => 'pending',
|
||
'assignee_id' => null,
|
||
'assignee_name' => '未分配',
|
||
'started_at' => null,
|
||
'submitted_at' => null,
|
||
'sla_deadline' => $estimated,
|
||
'is_overtime' => 0,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
}
|
||
|
||
private function insertInboundLogistics(int $orderId, array $logistics, string $now): void
|
||
{
|
||
$expressCompany = trim((string)($logistics['express_company'] ?? ''));
|
||
$trackingNo = trim((string)($logistics['tracking_no'] ?? ''));
|
||
if ($expressCompany === '' || $trackingNo === '') {
|
||
return;
|
||
}
|
||
|
||
$latestDesc = sprintf('客户已提交寄送运单:%s %s,等待鉴定中心签收。', $expressCompany, $trackingNo);
|
||
$logisticsId = (int)Db::name('order_logistics')->insertGetId([
|
||
'order_id' => $orderId,
|
||
'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,
|
||
]);
|
||
|
||
Db::name('order_logistics_nodes')->insert([
|
||
'logistics_id' => $logisticsId,
|
||
'node_time' => $now,
|
||
'node_desc' => $latestDesc,
|
||
'node_location' => '',
|
||
'created_at' => $now,
|
||
]);
|
||
}
|
||
|
||
private function formatLogistics(?array $logistics): ?array
|
||
{
|
||
if (!$logistics) {
|
||
return null;
|
||
}
|
||
|
||
return [
|
||
'express_company' => (string)$logistics['express_company'],
|
||
'tracking_no' => (string)$logistics['tracking_no'],
|
||
'tracking_status' => (string)$logistics['tracking_status'],
|
||
'latest_desc' => (string)$logistics['latest_desc'],
|
||
'latest_time' => (string)($logistics['latest_time'] ?? ''),
|
||
];
|
||
}
|
||
|
||
private function formatReturnAddress(array $item): array
|
||
{
|
||
return [
|
||
'consignee' => (string)($item['consignee'] ?? ''),
|
||
'mobile' => (string)($item['mobile'] ?? ''),
|
||
'province' => (string)($item['province'] ?? ''),
|
||
'city' => (string)($item['city'] ?? ''),
|
||
'district' => (string)($item['district'] ?? ''),
|
||
'detail_address' => (string)($item['detail_address'] ?? ''),
|
||
'full_address' => trim(sprintf(
|
||
'%s%s%s%s',
|
||
$item['province'] ?? '',
|
||
$item['city'] ?? '',
|
||
$item['district'] ?? '',
|
||
$item['detail_address'] ?? ''
|
||
)),
|
||
];
|
||
}
|
||
}
|