first
This commit is contained in:
174
server-api/app/support/MessageDispatcher.php
Normal file
174
server-api/app/support/MessageDispatcher.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace app\support;
|
||||
|
||||
use support\think\Db;
|
||||
|
||||
class MessageDispatcher
|
||||
{
|
||||
public function sendInboxEvent(string $eventCode, array $context): bool
|
||||
{
|
||||
$userId = (int)($context['user_id'] ?? 0);
|
||||
$bizType = trim((string)($context['biz_type'] ?? ''));
|
||||
$bizId = (int)($context['biz_id'] ?? 0);
|
||||
|
||||
if ($userId <= 0 || $bizType === '' || $bizId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->shouldSendByPreference($userId, $eventCode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rule = Db::name('message_rules')
|
||||
->where('event_code', $eventCode)
|
||||
->where('channel', 'inbox')
|
||||
->where('is_enabled', 1)
|
||||
->order('id', 'asc')
|
||||
->find();
|
||||
|
||||
if (!$rule) {
|
||||
$disabledRule = Db::name('message_rules')
|
||||
->where('event_code', $eventCode)
|
||||
->where('channel', 'inbox')
|
||||
->find();
|
||||
|
||||
if ($disabledRule) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$template = null;
|
||||
if ($rule) {
|
||||
$template = Db::name('message_templates')
|
||||
->where('id', (int)$rule['template_id'])
|
||||
->where('channel', 'inbox')
|
||||
->where('is_enabled', 1)
|
||||
->find();
|
||||
}
|
||||
|
||||
$alreadySent = Db::name('message_logs')
|
||||
->where('user_id', $userId)
|
||||
->where('biz_type', $bizType)
|
||||
->where('biz_id', $bizId)
|
||||
->where('channel', 'inbox')
|
||||
->where('status', 'sent')
|
||||
->find();
|
||||
|
||||
if ($alreadySent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$title = $this->renderTemplate(
|
||||
$template['title'] ?? (string)($context['fallback_title'] ?? ''),
|
||||
$context
|
||||
);
|
||||
$content = $this->renderTemplate(
|
||||
$template['content'] ?? (string)($context['fallback_content'] ?? ''),
|
||||
$context
|
||||
);
|
||||
|
||||
if ($title === '' && $content === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
Db::name('user_messages')->insert([
|
||||
'user_id' => $userId,
|
||||
'title' => $title !== '' ? $title : '系统通知',
|
||||
'content' => $content,
|
||||
'biz_type' => $bizType,
|
||||
'biz_id' => $bizId,
|
||||
'is_read' => 0,
|
||||
'read_at' => null,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
Db::name('message_logs')->insert([
|
||||
'user_id' => $userId,
|
||||
'template_id' => $template['id'] ?? null,
|
||||
'biz_type' => $bizType,
|
||||
'biz_id' => $bizId,
|
||||
'channel' => 'inbox',
|
||||
'status' => 'sent',
|
||||
'fail_reason' => '',
|
||||
'sent_at' => $now,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function shouldSendByPreference(int $userId, string $eventCode): bool
|
||||
{
|
||||
$preferenceKey = $this->preferenceKeyForEvent($eventCode);
|
||||
if ($preferenceKey === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$configValue = Db::name('system_configs')
|
||||
->where('config_group', 'user_settings')
|
||||
->where('config_key', 'user_' . $userId)
|
||||
->value('config_value');
|
||||
|
||||
if (!is_string($configValue) || $configValue === '') {
|
||||
return $this->defaultPreference($preferenceKey);
|
||||
}
|
||||
|
||||
$decoded = json_decode($configValue, true);
|
||||
if (!is_array($decoded)) {
|
||||
return $this->defaultPreference($preferenceKey);
|
||||
}
|
||||
|
||||
if (!array_key_exists($preferenceKey, $decoded)) {
|
||||
return $this->defaultPreference($preferenceKey);
|
||||
}
|
||||
|
||||
return (bool)$decoded[$preferenceKey];
|
||||
}
|
||||
|
||||
private function preferenceKeyForEvent(string $eventCode): string
|
||||
{
|
||||
return match ($eventCode) {
|
||||
'order_created' => 'notify_order',
|
||||
'return_shipped' => 'notify_order',
|
||||
'return_received' => 'notify_order',
|
||||
'report_published' => 'notify_report',
|
||||
'supplement_required' => 'notify_supplement',
|
||||
'ticket_reply', 'ticket_waiting_user', 'ticket_resolved', 'ticket_closed' => 'notify_ticket',
|
||||
default => '',
|
||||
};
|
||||
}
|
||||
|
||||
private function defaultPreference(string $key): bool
|
||||
{
|
||||
return match ($key) {
|
||||
'marketing_notify', 'privacy_mode' => false,
|
||||
default => true,
|
||||
};
|
||||
}
|
||||
|
||||
private function renderTemplate(string $text, array $context): string
|
||||
{
|
||||
if ($text === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return preg_replace_callback('/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/', function (array $matches) use ($context) {
|
||||
$key = $matches[1];
|
||||
if (!array_key_exists($key, $context)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$value = $context[$key];
|
||||
if (is_scalar($value) || $value === null) {
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
return '';
|
||||
}, $text) ?? $text;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user