Постоянно меняющиеся требования со стороны Центробанка и Росфинмониторинга заставляют финансовые организации уделять пристальное внимание своим клиентам с точки зрения их благонадежности. Каждый день производится ряд проверок, позволяющих оценить риски обслуживания организаций и физических лиц. В связи с этим появилась необходимость не просто автоматизировать данный процесс, но как-то его систематизировать и сделать удобным для сопровождения.

Общая схема комплексного анализа выглядит следующим образом:

Рис.1. Блок-схема комплексного анализа субъекта
Рис.1. Блок-схема комплексного анализа субъекта

На первом шаге вводятся данные анализируемого субъекта. Источником может выступать как внешний сервис, так и внутренняя база клиентов или ручной ввод. Далее выбираются контроли из списка или из категории. Процесс анализа заключается в последовательном выполнении всех проверок с сохранением результата и выводом в отчет. Проверка может выполнять различные действия, в основном это поиск по «неблагонадежным» справочникам, анализ истории операций и т.д. В связи с чем с точки зрения архитектуры реализацию данного процесса целесообразно вынести в хранимые процедуры СУБД, чтобы быть «поближе к данным».

В базе создаем справочники проверок и категорий, куда будем заводить собственные проверки.

Рис.2. ER-диаграмма БД
Рис.2. ER-диаграмма БД

В каждой проверке есть свой блок кода алгоритма, который будет динамически выполняться на каждой итерации. Результатом выполнения будет статус – пройдено/нет – и дополнительные показатели для вывода в отчет.

За запуск динамического кода отвечает процедура EXEC_PLSQL_BL. Все примеры реализации приведены на языке PL/SQL. На вход ей передается исполняемый код, куда биндится параметр – id субъекта:

PROCEDURE EXEC_PLSQL_BL(cErr OUT varchar2, iRef_id OUT NUMBER, iSubj_Id IN NUMBER, CSQLSTR IN CLOB)
IS
  iExecute  INTEGER;
  iCursor INTEGER := NULL;
BEGIN
  IF LENGTH(CSQLSTR) is not NULL THEN
      BEGIN
         IF NOT DBMS_SQL.Is_Open (iCursor) THEN
                iCursor := DBMS_SQL.Open_Cursor;
         end if;
         DBMS_SQL.Parse( iCursor, CSQLSTR, DBMS_SQL.Native );
         DBMS_SQL.Bind_Variable( iCursor, 'p1', iSubj_Id);
         DBMS_SQL.Bind_Variable( iCursor, 'o1', iRef_id);
         iExecute := DBMS_SQL.Execute( iCursor );
         DBMS_SQL.Variable_Value( iCursor, 'o1', iRef_id);
         DBMS_SQL.Close_Cursor(iCursor);
         EXCEPTION
          WHEN OTHERS THEN
              cERR:=SubStr(SQLERRM,1,255);
              DBMS_SQL.Close_Cursor(iCursor);
       END;
  END IF;

END EXEC_PLSQL_BL;

Здесь используется стандартный пакет Oracle DBMS_SQL, но можно применить и команду EXECUTE IMMEDIATE. Последняя обычно используется в случае большого количества ее вызовов.

Таблицы «Анализ» и «Совпадения» являются своего рода хранилищем истории проверок, а таблица «Субъекты» - база субъектов, когда-либо приходивших на обслуживание.

Процедура анализа субъекта выглядит следующим образом. На вход она получает id записи из таблицы «Анализ» и массив проверок (id из таблицы «Проверки»), которые нужно выполнить. Перед ее вызовом происходит заведение новых сущностей в таблицы «Анализ» и «Субъекты».  

PROCEDURE analyze_sbj(cErr OUT varchar2, p_chk_id IN NUMBER, p_mrk_id IN T_TAB_NUMBERS)
IS
   vRefId NUMBER;
   vSubjId NUMBER := SBJ_CTRL_UTL.Get_Chk_Subj_Id(p_chk_id); -- получить id cубъекта
   vCntRefBuff INTEGER := 0; -- заполнен ли буфер совпадений
BEGIN
    FOR rCtrl IN (SELECT c.* FROM sbj_ctrl c
                    JOIN TABLE(p_mrk_id) t ON VALUE(t) = c.iid
                    ORDER BY c.iid
                 )
    LOOP
        delete from rp_id_tmp; -- Очищаем буфер с совпадениями
        IF NOT EXEC_PLSQL_BL(cErr,vRefId,vSubjId,rCtrl.cPL) THEN
            cErr := 'При выполнении проверки ID="' ||rCtrl.IID|| '" произошла ошибка: ' || cErr;
            raise_application_error(-20000,cErr);
            exit;
        ELSE -- сохраняем проверку
            SELECT 
              CASE WHEN EXISTS (SELECT NULL FROM rp_id_tmp) THEN 1
                   ELSE 0
              END 
            INTO vCntRefBuff
            FROM dual;
            
            IF vCntRefBuff > 0 THEN
               FOR rRef IN (SELECT inum FROM rp_id_tmp) LOOP
                   SBJ_CTRL_FLR.Ins_Sbj_Suspect(p_chk_id, rCtrl.IID, rRef.Inum);
               END LOOP;
            ELSE
               SBJ_CTRL_FLR.Ins_Sbj_Suspect(p_chk_id, rCtrl.IID, vRefId);
            END IF;
        END IF;
    END LOOP;
END; 

Процедура в цикле запускает проверки и сохраняет результаты в журнале выявлений. На основе полученных данных можно реализовать любой отчет в требуемом бизнесу виде.

Полученный механизм организации комплексного анализа заметно облегчил поддержку в актуальном состоянии необходимого перечня проверок над субъектами, а также позволил хранить историю.

З.Ы. При настройке проверок на совпадения с различными списками можно пользоваться решением, описанным в статье «Справочник объектов поиска и анализа».