229 lines
9.1 KiB
PHP
229 lines
9.1 KiB
PHP
<?php
|
|
|
|
namespace app\support;
|
|
|
|
use support\think\Db;
|
|
|
|
class EnterpriseWebhookService
|
|
{
|
|
public function recordOrderEvent(int $orderId, string $eventCode, array $data = [], bool $sendNow = true): ?array
|
|
{
|
|
$ref = Db::name('enterprise_customer_order_refs')->where('order_id', $orderId)->find();
|
|
if (!$ref) {
|
|
return null;
|
|
}
|
|
|
|
$customer = Db::name('enterprise_customers')->where('id', (int)$ref['customer_id'])->find();
|
|
$order = Db::name('orders')->where('id', $orderId)->find();
|
|
if (!$customer || !$order) {
|
|
return null;
|
|
}
|
|
|
|
$eventMeta = $this->eventMeta($eventCode);
|
|
$payload = [
|
|
'event_code' => $eventCode,
|
|
'event_text' => $eventMeta['event_text'],
|
|
'customer_id' => (string)$customer['customer_code'],
|
|
'customer_code' => (string)$customer['customer_code'],
|
|
'external_order_no' => (string)$ref['external_order_no'],
|
|
'order_no' => (string)$order['order_no'],
|
|
'appraisal_no' => (string)$order['appraisal_no'],
|
|
'status_code' => $eventMeta['status_code'],
|
|
'status_text' => $eventMeta['status_text'],
|
|
'occurred_at' => date('Y-m-d H:i:s'),
|
|
'data' => $data,
|
|
];
|
|
|
|
$eventId = (int)Db::name('enterprise_order_events')->insertGetId([
|
|
'customer_id' => (int)$customer['id'],
|
|
'order_id' => $orderId,
|
|
'external_order_no' => (string)$ref['external_order_no'],
|
|
'event_code' => $eventCode,
|
|
'event_text' => $eventMeta['event_text'],
|
|
'status_code' => $eventMeta['status_code'],
|
|
'status_text' => $eventMeta['status_text'],
|
|
'payload_json' => json_encode($payload, JSON_UNESCAPED_UNICODE),
|
|
'occurred_at' => $payload['occurred_at'],
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
]);
|
|
|
|
$payload['event_id'] = $eventId;
|
|
|
|
if ($sendNow) {
|
|
$this->deliverEvent($eventId, false);
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
public function deliverEvent(int $eventId, bool $manual = false): array
|
|
{
|
|
$event = Db::name('enterprise_order_events')->where('id', $eventId)->find();
|
|
if (!$event) {
|
|
throw new \RuntimeException('事件不存在');
|
|
}
|
|
|
|
$customer = Db::name('enterprise_customers')->where('id', (int)$event['customer_id'])->find();
|
|
if (!$customer) {
|
|
throw new \RuntimeException('客户不存在');
|
|
}
|
|
|
|
$url = trim((string)($customer['webhook_url'] ?? ''));
|
|
$appKey = (string)Db::name('enterprise_customer_apps')
|
|
->where('customer_id', (int)$customer['id'])
|
|
->where('status', 'enabled')
|
|
->order('id', 'asc')
|
|
->value('app_key');
|
|
$attemptNo = (int)Db::name('enterprise_webhook_deliveries')->where('event_id', $eventId)->count() + 1;
|
|
|
|
if (!(bool)($customer['webhook_enabled'] ?? false) || $url === '' || $appKey === '') {
|
|
$delivery = $this->saveDelivery($event, $url, $appKey, $attemptNo, 'skipped', 0, '', 'Webhook未启用或配置不完整', $manual);
|
|
return ['delivery' => $delivery, 'sent' => false];
|
|
}
|
|
|
|
$payload = json_decode((string)$event['payload_json'], true);
|
|
if (!is_array($payload)) {
|
|
$payload = [];
|
|
}
|
|
$payload['event_id'] = $eventId;
|
|
|
|
$result = $this->postJson($url, $payload, $appKey);
|
|
$status = ($result['http_status'] >= 200 && $result['http_status'] < 300 && $result['error_message'] === '') ? 'success' : 'failed';
|
|
|
|
$delivery = $this->saveDelivery(
|
|
$event,
|
|
$url,
|
|
$appKey,
|
|
$attemptNo,
|
|
$status,
|
|
$result['http_status'],
|
|
$result['response_body'],
|
|
$result['error_message'],
|
|
$manual
|
|
);
|
|
|
|
return ['delivery' => $delivery, 'sent' => $status === 'success'];
|
|
}
|
|
|
|
public function formatEvent(array $item): array
|
|
{
|
|
return [
|
|
'id' => (int)$item['id'],
|
|
'customer_id' => (int)$item['customer_id'],
|
|
'order_id' => (int)$item['order_id'],
|
|
'external_order_no' => (string)$item['external_order_no'],
|
|
'event_code' => (string)$item['event_code'],
|
|
'event_text' => (string)$item['event_text'],
|
|
'status_code' => (string)$item['status_code'],
|
|
'status_text' => (string)$item['status_text'],
|
|
'occurred_at' => (string)$item['occurred_at'],
|
|
'created_at' => (string)$item['created_at'],
|
|
];
|
|
}
|
|
|
|
public function formatDelivery(array $item): array
|
|
{
|
|
return [
|
|
'id' => (int)$item['id'],
|
|
'event_id' => (int)$item['event_id'],
|
|
'customer_id' => (int)$item['customer_id'],
|
|
'webhook_url' => (string)$item['webhook_url'],
|
|
'app_key' => (string)$item['app_key'],
|
|
'attempt_no' => (int)$item['attempt_no'],
|
|
'delivery_status' => (string)$item['delivery_status'],
|
|
'delivery_status_text' => $this->deliveryStatusText((string)$item['delivery_status']),
|
|
'http_status' => (int)$item['http_status'],
|
|
'response_body' => (string)($item['response_body'] ?? ''),
|
|
'error_message' => (string)($item['error_message'] ?? ''),
|
|
'is_manual' => (bool)($item['is_manual'] ?? false),
|
|
'sent_at' => (string)($item['sent_at'] ?? ''),
|
|
'created_at' => (string)($item['created_at'] ?? ''),
|
|
];
|
|
}
|
|
|
|
private function eventMeta(string $eventCode): array
|
|
{
|
|
return match ($eventCode) {
|
|
'order_created' => ['event_text' => '订单创建', 'status_code' => 'pending_shipping', 'status_text' => '待寄送商品'],
|
|
'inbound_received' => ['event_text' => '快递已到仓', 'status_code' => 'received', 'status_text' => '鉴定中心已收货'],
|
|
'appraising' => ['event_text' => '物品鉴定中', 'status_code' => 'appraising', 'status_text' => '物品鉴定中'],
|
|
'appraisal_finished' => ['event_text' => '物品鉴定完成', 'status_code' => 'generating_report', 'status_text' => '物品鉴定完成'],
|
|
'report_published' => ['event_text' => '报告已发布', 'status_code' => 'report_published', 'status_text' => '报告已发布'],
|
|
'return_shipped' => ['event_text' => '物品已寄回', 'status_code' => 'return_shipped', 'status_text' => '物品已寄回'],
|
|
'completed' => ['event_text' => '订单已完成', 'status_code' => 'completed', 'status_text' => '已完成'],
|
|
'supplement_required' => ['event_text' => '需要补充资料', 'status_code' => 'pending_supplement', 'status_text' => '需要补充资料'],
|
|
default => ['event_text' => $eventCode, 'status_code' => $eventCode, 'status_text' => $eventCode],
|
|
};
|
|
}
|
|
|
|
private function postJson(string $url, array $payload, string $appKey): array
|
|
{
|
|
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => $body,
|
|
CURLOPT_TIMEOUT => 6,
|
|
CURLOPT_CONNECTTIMEOUT => 3,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Content-Type: application/json',
|
|
'X-AXY-App-Key: ' . $appKey,
|
|
],
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$errno = curl_errno($ch);
|
|
$error = curl_error($ch);
|
|
$httpStatus = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
return [
|
|
'http_status' => $httpStatus,
|
|
'response_body' => is_string($response) ? substr($response, 0, 2000) : '',
|
|
'error_message' => $errno ? $error : '',
|
|
];
|
|
}
|
|
|
|
private function saveDelivery(
|
|
array $event,
|
|
string $url,
|
|
string $appKey,
|
|
int $attemptNo,
|
|
string $status,
|
|
int $httpStatus,
|
|
string $responseBody,
|
|
string $errorMessage,
|
|
bool $manual
|
|
): array {
|
|
$now = date('Y-m-d H:i:s');
|
|
$id = (int)Db::name('enterprise_webhook_deliveries')->insertGetId([
|
|
'event_id' => (int)$event['id'],
|
|
'customer_id' => (int)$event['customer_id'],
|
|
'webhook_url' => $url,
|
|
'app_key' => $appKey,
|
|
'attempt_no' => $attemptNo,
|
|
'delivery_status' => $status,
|
|
'http_status' => $httpStatus,
|
|
'response_body' => $responseBody,
|
|
'error_message' => $errorMessage,
|
|
'is_manual' => $manual ? 1 : 0,
|
|
'sent_at' => $status === 'skipped' ? null : $now,
|
|
'created_at' => $now,
|
|
'updated_at' => $now,
|
|
]);
|
|
|
|
return Db::name('enterprise_webhook_deliveries')->where('id', $id)->find() ?: [];
|
|
}
|
|
|
|
private function deliveryStatusText(string $status): string
|
|
{
|
|
return match ($status) {
|
|
'success' => '推送成功',
|
|
'failed' => '推送失败',
|
|
'skipped' => '已跳过',
|
|
default => $status,
|
|
};
|
|
}
|
|
}
|