This commit is contained in:
wushumin
2026-05-11 15:28:27 +08:00
commit edd1a02157
302 changed files with 67193 additions and 0 deletions

View File

@@ -0,0 +1,228 @@
<?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,
};
}
}