Статья №155
Вариант решения задачи по перезвонам абонентов в с использованием метода работы с таблицей "Кеш внутри БД"
На примере рассмотрен вопрос организации сложного механизма (перезвон абонентам в индивидуальное время на конкретный телефон) перезвонов с использованием механизмов работы с таблицей.
В Октеле существует 2 вида работы с таблицей абонентов:
Загрузка в память и синхронизация,
Кеш внутри БД.
В первом случае все алгоритмы работы с базой абонентов полностью определятеся несколькими опциями по обходу списка абонентов, которые жёстко заложены в программу. В ряде случаев их бывает недостаточно. Тут нам на помощь может прийти второй метод работы с таблицей абонентов в рамках задачи.
Рассмотрим для начала структуру таблиц кеша внутри БД (здесь и далее база данных Октел):
Сам кеш для каждой задачи располагается в таблице вида A_TaskManager_Idx_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX , где XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX — это ID нашей задачи. ID задачи узнаём из таблицы A_TaskManager_Tasks.
Поля:
• [Id] [int] — индекс в таблице абонентов;
• [AC] [int] — количество «просмотров» записи в таблице абонентов;
• [State] [int] — состояние: 0 не звонили, 1 позвонили удачно, 2 звонили неудачно и достигли предела количества попыток, 3 больше не звонить по записи;
• [NIdx] [int] — индекс номера, который следующим надо будет брать из общего списка номеров абонента в таблице абонентов.
Данные в кеше обнавляются по таймауту указанному в параметре задачи «Интервал синхронизации». При появлении в таблице абонентов новой записи при синхронизации в кеш добавляется строка с соответствующим ID и равными нулю остальными полями. При исчезновении записи из таблицы абонентов поле state записи с соответствующим ID становится равным 3. При выборке из таблицы абонентов записи для соответствущей записи в кеше поле АС увеличивается на 1.
2. Задача
В качестве базовой задачи для примера осуществления перезвона в рамках исходящей кампании возьмём следующую задачу:
Имеется таблица абонентов, в таблице абонентов указано предпочтительное время звонка каждому абоненту (для каждого абонента время звонка строго индивидуально). Абоненту может потребоваться перезвон на другой номер в нужное время. Необходимо обзвонить всех абонентов, а так же организовать необходимое количество перезвонов для каждого абонента (число перезвонов для абонента - произвольно)
3. Организация таблиц для перезвона
Процесс первоначального дозвона и перезвона организовывается в рамках одной и той же исходящей задачи. Для организации перезвона необходимо организовать следующие таблицы:
Справочник абонентов. По сути это и есть наш список абонентов, которым необходимо звонить. Должен иметь следующие обязательные поля:
• идентификатор записи (тип поля не имеет значения);
• идентификатор абонента (в общем случае абоненту может соответствовать несколько записей в справочнике абонентов, с помощью этого приёма для каждого абонента мы можем создать произвольное количество телефонных номеров для обзвона);
• Наименование абонента;
• поля с телефонами для дозвона — может быть несколько штук;
• служебные поля для управления процессом обзвона абонентов.
Таблица перезвона. Представляет собой набор задач, по которым необходимо сделать телефонный вызов. Должен иметь следующие поля:
• идентификатор записи (int – для стыковки с таблицей кеша);
• ссылка на идентификатор справочника абонентов;
• телефон для обзвона;
• набор полей для активации записи для прозвона прозвоне.
На основе этих 2х таблиц генерируется таблица абонентов передаваемая в Октел (я это делаю с помощью view). При данной организации происходит унификация первоначальных звонков и перезвонов в рамках одной задачи на прозвон абонентов.
4. Решение
Имеем таблицу абонентов:
CREATE TABLE [dbo].[is_alex_kls](
[id] [uniqueidentifier] NOT NULL, --идентификатор абонента
[fio] [varchar](50) NOT NULL, --фио
[doljnost] [varchar](50) NOT NULL, -- должность
[company] [varchar](50) NOT NULL, -- фирма
[phone] [varchar](20) NOT NULL, --телефон
[hours] [int] NOT NULL, -- предпочтительное время перезвона
CONSTRAINT [PK_is_alex_kls] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Механизм формирования поля часов прозвона следующий: часы прозвона выступают в виде битовой маски из 3х байт (24 часа) с указанием в 1 тех битов, которым соответствуют часы прозвона. Т.е. если нам надо звонить с 9.00 до 10.00 и с 14.00 до 15.00 то значение будет следующее: 00000000 00100001 00000000 = 8448 . Разбиение временного суточного интервала в шагом в час связано с тем, что при массовом дозвоне сложно попасть в маленький временной интервал.
Формируем таблицу перезвона:
CREATE TABLE [dbo].[is_alex_recall](
[id] [int] NOT NULL, --идентификатор записи для октела
[kls] [uniqueidentifier] NOT NULL, --ссылка на поле в справочнике
[phonerecall] [varchar](20) NOT NULL, --телефон для перезвона
[daterecall] [datetime] NOT NULL, --дата перезвона
[hourrecall] [int] NOT NULL,--часы прозвона для перезвона
[status] [int] NOT NULL,--статус активности задачи на перезвон
CONSTRAINT [PK_is_alex_recall] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
В качестве таблицы абонентов передаваемой непосредственно в октел будем использовать просмотр:
CREATE VIEW V_IS_RECALL
AS
SELECT [recall].[id] ---идентификатор в таблице абонентов октела
,[recall].[phonerecall]--телефон в таблице абонентов октела
,[kls].[doljnost] --информационное поле в таблице абонентов
,[kls].[fio] -- наименование в таблице абонентов
,[kls].[company]--информационное поле в таблице
FROM [alexraznoe].[dbo].[is_alex_recall] recall
inner join [alexraznoe].[dbo].[is_alex_kls] kls on recall.kls=kls.id
where getdate() between daterecall and dateadd(dd,1,daterecall) -- проверяем попадаем ли в нужную дату для звонка
and (datepart(hh,getdate())&[recall].[hourrecall]>0) -- проверяем попадаем ли в часы
Как видно из запроса абонент будет периодически то пропадать в списке на прозвон то появляться (попадание в часы обзвона)
Для этого мы должны сделать служебный сценарий (запускать можно периодически, например раз в 10 минут), в котором выправляем поле state для таблицы кеша со значения 3 на 0.:
update [A_TaskManager_Idx_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX]
set state=0
where id in(
SELECT [id]
FROM [alexraznoe].[dbo].[IS_RECALL] recall
LEFT join
( SELECT [IdInList]
FROM [oktell_cc_temp].[dbo].[A_Cube_CC_EffortConnections]
where idtask='XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' and
idchain is not null and idoperator<>'AB000000-0000-0000-0000-000000000000' ) tmp
on tmp.idinlist=recall.id
where tmp.idinlist is null
)
Т.е. Восстанавливаем state=0 всем задачам которые в активной таблице обзвона и по которым ни разу на дозвонились.
При необходимости перезвона абоненту происходит вставка задачи на перезвон в таблицу. [dbo].[is_alex_recall] с указанием: даты перезвона, предпочтительного времени перезвона, телефона, по которому осуществлять перезвон. Так же можно в таблице перезвонов указывать другие справочные данные, например ФИО нового контактного лица.
Крайне полезно для контроля выполнения перезвонов осуществлять привязку строки генерирующей перезвон со звонком, создавшим задачу на перезвон. Это можно сделать введением дополнительного uniqueidentifier-поля с ID цепочки комутаций сгенерировавшей задание на перезвон.
При необходимости исключить задание на перезвон, например в случае если был произведён один успешный перезвон и по остальным перезвонам абоненту звонить более не нужно — можно для данного абонента обнулить поле hourrecall таблицы [dbo].[is_alex_recall] для всех последующих заданий на перезвон (т.е. С большим ID).
Исходящая задача, Кеш внутри БД, перезвон