added table
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { chunk } from 'lodash'
|
||||
import { createSMS } from '@/api/sms'
|
||||
|
||||
export interface GroupSmsSendError {
|
||||
batch: number
|
||||
numbers: string[]
|
||||
error: string
|
||||
}
|
||||
|
||||
export type GroupSmsSendStatus = 'idle' | 'running' | 'done' | 'canceled'
|
||||
export type GroupSmsSendOutcome = 'running' | 'success' | 'error' | 'partial' | 'canceled'
|
||||
|
||||
/** هر درخواست حداکثر ۱۰ شماره — پنل پیامک زیر فشار نرود */
|
||||
const BATCH_SIZE = 10
|
||||
|
||||
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
||||
|
||||
/**
|
||||
* ارسال گروهی پیامک — مثل ایمپورت مشتری حسابداری:
|
||||
* در پسزمینه ادامه مییابد، مینیمایز میشود، ۱۰تایی پیش میرود.
|
||||
*/
|
||||
export const useGroupSmsSendStore = defineStore('groupSmsSend', () => {
|
||||
const status = ref<GroupSmsSendStatus>('idle')
|
||||
const messageText = ref('')
|
||||
const queue = ref<string[]>([])
|
||||
const total = ref(0)
|
||||
const processed = ref(0)
|
||||
const success = ref(0)
|
||||
const failed = ref(0)
|
||||
const errors = ref<GroupSmsSendError[]>([])
|
||||
const currentBatch = ref(0)
|
||||
const totalBatches = ref(0)
|
||||
|
||||
const visible = ref(false)
|
||||
const minimized = ref(false)
|
||||
|
||||
let cancelRequested = false
|
||||
|
||||
const active = computed(() => status.value === 'running')
|
||||
const finished = computed(() => status.value === 'done' || status.value === 'canceled')
|
||||
const percent = computed(() =>
|
||||
total.value > 0 ? Math.min(100, Math.round((processed.value / total.value) * 100)) : 0,
|
||||
)
|
||||
const currentLabel = computed(() => {
|
||||
if (!active.value) return ''
|
||||
if (totalBatches.value <= 0) return '...'
|
||||
return `دسته ${Math.min(currentBatch.value + 1, totalBatches.value)} از ${totalBatches.value}`
|
||||
})
|
||||
|
||||
const outcome = computed<GroupSmsSendOutcome>(() => {
|
||||
if (status.value === 'running' || status.value === 'idle') return 'running'
|
||||
if (status.value === 'canceled') return 'canceled'
|
||||
if (failed.value === 0) return 'success'
|
||||
if (success.value === 0) return 'error'
|
||||
return 'partial'
|
||||
})
|
||||
|
||||
function reset() {
|
||||
status.value = 'idle'
|
||||
messageText.value = ''
|
||||
queue.value = []
|
||||
total.value = 0
|
||||
processed.value = 0
|
||||
success.value = 0
|
||||
failed.value = 0
|
||||
errors.value = []
|
||||
currentBatch.value = 0
|
||||
totalBatches.value = 0
|
||||
visible.value = false
|
||||
minimized.value = false
|
||||
cancelRequested = false
|
||||
}
|
||||
|
||||
function expand() {
|
||||
minimized.value = false
|
||||
}
|
||||
|
||||
function minimize() {
|
||||
minimized.value = true
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
if (status.value === 'running') cancelRequested = true
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
if (status.value === 'running') return
|
||||
reset()
|
||||
}
|
||||
|
||||
async function start(numbers: string[], message: string) {
|
||||
if (status.value === 'running') return
|
||||
|
||||
const unique = Array.from(new Set(numbers.map((n) => String(n).trim()).filter(Boolean)))
|
||||
if (!unique.length || !message.trim()) return
|
||||
|
||||
messageText.value = message
|
||||
queue.value = unique
|
||||
total.value = unique.length
|
||||
processed.value = 0
|
||||
success.value = 0
|
||||
failed.value = 0
|
||||
errors.value = []
|
||||
currentBatch.value = 0
|
||||
totalBatches.value = Math.ceil(unique.length / BATCH_SIZE)
|
||||
cancelRequested = false
|
||||
status.value = 'running'
|
||||
visible.value = true
|
||||
minimized.value = false
|
||||
|
||||
await runLoop()
|
||||
}
|
||||
|
||||
async function sendBatchWithRetry(batchNumbers: string[], attempt = 0): Promise<void> {
|
||||
try {
|
||||
await createSMS({
|
||||
message: messageText.value,
|
||||
numbers: batchNumbers
|
||||
})
|
||||
} catch (e) {
|
||||
if (attempt < 2 && !cancelRequested) {
|
||||
await sleep(1500 * (attempt + 1))
|
||||
return sendBatchWithRetry(batchNumbers, attempt + 1)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function runLoop() {
|
||||
const batches = chunk(queue.value, BATCH_SIZE)
|
||||
|
||||
for (let i = 0; i < batches.length; i++) {
|
||||
if (cancelRequested) {
|
||||
status.value = 'canceled'
|
||||
return
|
||||
}
|
||||
|
||||
currentBatch.value = i
|
||||
const batchNumbers = batches[i]
|
||||
|
||||
try {
|
||||
await sendBatchWithRetry(batchNumbers)
|
||||
success.value += batchNumbers.length
|
||||
} catch {
|
||||
failed.value += batchNumbers.length
|
||||
errors.value.push({
|
||||
batch: i + 1,
|
||||
numbers: batchNumbers,
|
||||
error: 'خطا در ارتباط با پنل پیامک'
|
||||
})
|
||||
}
|
||||
|
||||
processed.value += batchNumbers.length
|
||||
if (i < batches.length - 1) await sleep(250)
|
||||
}
|
||||
|
||||
status.value = 'done'
|
||||
}
|
||||
|
||||
return {
|
||||
status,
|
||||
messageText,
|
||||
queue,
|
||||
total,
|
||||
processed,
|
||||
success,
|
||||
failed,
|
||||
errors,
|
||||
currentBatch,
|
||||
totalBatches,
|
||||
visible,
|
||||
minimized,
|
||||
active,
|
||||
finished,
|
||||
percent,
|
||||
currentLabel,
|
||||
outcome,
|
||||
start,
|
||||
cancel,
|
||||
dismiss,
|
||||
expand,
|
||||
minimize,
|
||||
reset
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user