added trigger for chat sms

This commit is contained in:
2026-08-04 18:59:52 +03:30
parent 45647e7266
commit e8d5dc9f66
3 changed files with 105 additions and 2 deletions
+17
View File
@@ -0,0 +1,17 @@
import useAxios from '@core/utils/useAxios'
/** اعلام تمرکز ادمین روی اتاق پشتیبانی (برای جلوگیری از SMS وقتی ادمین همان چت را می‌بیند) */
export const postAdminChatFocus = async (payload: {
room_id?: string | null
previous_room_id?: string | null
}) =>
await useAxios.post('/admin/chat/focus', {
room_id: payload.room_id || '',
previous_room_id: payload.previous_room_id || ''
})
export const postAdminChatUnfocus = async (room_id: string) =>
await useAxios.post('/admin/chat/unfocus', { room_id })
export const postAdminChatEnsureWebhook = async () =>
await useAxios.post('/admin/chat/ensure-webhook')
@@ -317,6 +317,7 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
sms_notif_fund_withdraw_customer: 'switch',
sms_notif_price_trigger: 'switch',
sms_notif_wallet_alert: 'switch',
sms_notif_support_admin: 'switch',
group_sms: 'switch',
montzar_closed: 'switch',
montzar_open_buy: 'switch',
@@ -798,6 +799,8 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
'روشن: وقتی هشدار قیمت مشتری (اقدام=پیامک) فعال شود، پیامک به مشتری می‌رود. خاموش: هشدار قیمت پیامک نمی‌فرستد.',
sms_notif_wallet_alert:
'روشن: وقتی اعتبار پنل پیامک صفر یا منفی شود، هشدار به سوپرادمین و ادمین‌ها می‌رود. خاموش: این هشدار نمی‌رود.',
sms_notif_support_admin:
'روشن: وقتی مشتری اولین پیام خوانده‌نشده را در چت پشتیبانی می‌فرستد و ادمین همان گفتگو را باز و visible ندارد، پیامک به ادمین‌های دارای «ارسال پیامک» می‌رود. خاموش: اعلان پیامک چت نمی‌رود.',
group_sms:
'روشن: امکان ارسال پیامک گروهی از پنل ادمین فعال است. خاموش: ارسال پیامک گروهی غیرفعال است.',
profit_gold_api_enabled:
@@ -977,6 +980,7 @@ export const KEY_LABEL_FALLBACK: Record<string, string> = {
pargar_item_id_rial: 'کد کالای ریال پرگار',
pargar_item_id_gold: 'کد کالای طلا پرگار',
sms_notif_wallet_alert: 'پیامک هشدار اعتبار پنل پیامک',
sms_notif_support_admin: 'پیامک پیام پشتیبانی به ادمین',
auto_active_customer: 'فعال‌سازی خودکار پس از ثبت‌نام',
balance_report_exclude_customer_id: 'حذف مشتری از گزارش تراز',
chart_use_local_data: 'نمودار از داده داخلی',
@@ -1097,7 +1101,7 @@ export const SETTING_EDITABLE_KEYS = new Set([
'sms_notif_gateway_charge_admin', 'sms_notif_register_customer', 'sms_notif_register_admin',
'sms_notif_jibit_admin', 'sms_notif_bank_card_admin', 'sms_notif_bank_card_customer',
'sms_notif_fund_withdraw_admin', 'sms_notif_fund_withdraw_customer',
'sms_notif_price_trigger', 'sms_notif_wallet_alert',
'sms_notif_price_trigger', 'sms_notif_wallet_alert', 'sms_notif_support_admin',
'deposit_sms_support_number', 'deposit_support_message_text',
'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
'endDay',
+83 -1
View File
@@ -2,6 +2,7 @@ import { computed, ref, watch } from 'vue'
import { useWebSocket } from '@vueuse/core'
import { useUserStore } from '@/store/userStore'
import { createUuid } from '@/utils/index'
import { postAdminChatFocus, postAdminChatUnfocus, postAdminChatEnsureWebhook } from '@/api/chatFocus'
type RawChatRoom = Record<string, any>
type RawChatMessage = Record<string, any>
@@ -29,6 +30,7 @@ const MAX_RESEND_ATTEMPTS = 1
const CONNECTING_TIMEOUT_MS = 12000
const RECONNECT_WATCH_INTERVAL_MS = 5000
const READ_SIGNAL_DEBOUNCE_MS = 1200
const FOCUS_HEARTBEAT_MS = 20000
type AdminOutgoingMessageType = 'text' | 'image' | 'voice'
@@ -70,6 +72,8 @@ const lastReadSignalAt = ref(0)
const lastReadSignalRoomId = ref('')
let readSignalTimer: number | null = null
let pendingAdminReadSignalRoomId = ''
let focusHeartbeatTimer: number | null = null
let lastReportedFocusRoomId = ''
let bootstrapPromise: Promise<void> | null = null
@@ -132,7 +136,12 @@ export const setAdminSupportPageOpen = (value: boolean) => {
isAdminActiveRoomAtBottom.value = false
clearReadSignalTimer()
pendingAdminReadSignalRoomId = ''
const previousRoom = activeRoomId.value || lastReportedFocusRoomId
activeRoomId.value = ''
stopFocusHeartbeat()
if (previousRoom) {
void syncAdminChatFocus(null, previousRoom)
}
return
}
@@ -146,6 +155,8 @@ export const setAdminSupportPageOpen = (value: boolean) => {
scheduleAdminReadSignal(80)
}
refreshFocusHeartbeat()
if (value && !manuallyClosed && status.value !== 'OPEN') {
safeReconnect('admin support page opened').catch((error) => {
console.error('admin reconnect on page open failed:', error)
@@ -164,6 +175,63 @@ const isViewingRoomNow = (roomId: string) => {
)
}
const syncAdminChatFocus = async (roomId: string | null, previousRoomId?: string) => {
try {
if (!roomId) {
if (previousRoomId || lastReportedFocusRoomId) {
await postAdminChatUnfocus(previousRoomId || lastReportedFocusRoomId)
}
lastReportedFocusRoomId = ''
return
}
await postAdminChatFocus({
room_id: roomId,
previous_room_id: previousRoomId && previousRoomId !== roomId ? previousRoomId : undefined
})
lastReportedFocusRoomId = roomId
} catch (error) {
console.error('admin chat focus sync failed:', error)
}
}
const stopFocusHeartbeat = () => {
if (focusHeartbeatTimer !== null) {
window.clearInterval(focusHeartbeatTimer)
focusHeartbeatTimer = null
}
}
const refreshFocusHeartbeat = () => {
stopFocusHeartbeat()
const roomId = activeRoomId.value
if (!isViewingRoomNow(roomId)) {
if (lastReportedFocusRoomId) {
void syncAdminChatFocus(null, lastReportedFocusRoomId)
}
return
}
void syncAdminChatFocus(
roomId,
lastReportedFocusRoomId && lastReportedFocusRoomId !== roomId
? lastReportedFocusRoomId
: undefined
)
focusHeartbeatTimer = window.setInterval(() => {
if (!isViewingRoomNow(activeRoomId.value)) {
stopFocusHeartbeat()
if (lastReportedFocusRoomId) {
void syncAdminChatFocus(null, lastReportedFocusRoomId)
}
return
}
void syncAdminChatFocus(activeRoomId.value)
}, FOCUS_HEARTBEAT_MS)
}
const canTreatAdminRoomAsRead = (roomId = activeRoomId.value) => {
return Boolean(
roomId &&
@@ -1032,7 +1100,13 @@ const bindNetworkListeners = () => {
})
document.addEventListener('visibilitychange', () => {
if (document.visibilityState !== 'visible') return
if (document.visibilityState !== 'visible') {
stopFocusHeartbeat()
if (lastReportedFocusRoomId) {
void syncAdminChatFocus(null, lastReportedFocusRoomId)
}
return
}
if (isAdminSupportPageOpen.value && activeRoomId.value) {
if (canTreatAdminRoomAsRead(activeRoomId.value)) {
@@ -1041,6 +1115,8 @@ const bindNetworkListeners = () => {
}
}
refreshFocusHeartbeat()
if (!shouldKeepAdminSocketAlive()) return
if (status.value !== 'OPEN') {
@@ -1435,6 +1511,8 @@ export const setAdminActiveRoom = async (
pendingAdminReadSignalRoomId = ''
}
refreshFocusHeartbeat()
if (isViewingRoomNow(roomId)) {
clearRoomUnread(roomId)
}
@@ -1609,6 +1687,10 @@ export const bootstrapAdminChat = async (
bootstrapPromise = (async () => {
try {
void postAdminChatEnsureWebhook().catch((error) => {
console.error('admin chat ensure webhook failed:', error)
})
await fetchAdminRooms()
const shouldConnect = connectEvenIfNoRooms || rooms.value.length > 0