Advertisement
Guest User

Untitled

a guest
Mar 29th, 2020
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PL/SQL 17.99 KB | None | 0 0
  1. CREATE OR REPLACE EDITIONABLE TRIGGER "CASEMGMT"."ECM_ENTITY_LOCK_TRIGGER"
  2. -- Срабатывает на вставку и удаление
  3. FOR INSERT OR DELETE ON CASEMGMT.ECM_ENTITY_LOCK
  4. -- Триггер составной для обхода ошибки mutating table
  5. COMPOUND TRIGGER
  6.   -- numberList_type is table of number
  7.   -- Список блокировок заявок (RK) на удаление
  8.   g_c_delete_list NumberList_type := NumberList_type();
  9.   -- Список инцидентов (RK) на блокировку
  10.   g_i_insert_list NumberList_type := NumberList_type();
  11.   -- Список блокировок инцидентов (RK) на удаление
  12.   g_i_delete_list NumberList_type := NumberList_type();
  13.   -- Пользователь, который устанавливает или снимает блокировку
  14.   g_lock_user_id VARCHAR2(60 CHAR);
  15.  
  16.   -- Секция, которая срабатывает для каждой строки
  17.   -- здесь только составляются списки (g_*_list) на вставку и удаление
  18.   AFTER EACH ROW IS
  19.     -- Номер заявки, с которой связана блокировка
  20.     v_case_rk NUMBER(10, 0);
  21.     -- Верификатор, с которым связана заявка
  22.     v_case_investigator VARCHAR2(60 CHAR);
  23.     -- Исключение для выхода из секции, если входные условия не подходят
  24.     do_nothing EXCEPTION;
  25.   BEGIN
  26.     -- Если блокировка снимается
  27.     IF DELETING THEN
  28.       -- Запомним, чья это была блокировка
  29.       g_lock_user_id := :old.LOCK_USER_ID;
  30.  
  31.       BEGIN
  32.         -- В зависимости от типа разблокируемой сущности определим связанную заявку
  33.         CASE :old.ENTITY_NM
  34.           -- Разблокируется заявка - сразу имеем её RK
  35.           WHEN 'CASE' THEN v_case_rk := :old.ENTITY_RK;
  36.           -- Разблокируется инцидент - запросим RK в INCIDENT_LIVE
  37.           WHEN 'INCIDENT' THEN
  38.             SELECT CASE_RK INTO v_case_rk FROM CASEMGMT.INCIDENT_LIVE WHERE INCIDENT_RK = :old.ENTITY_RK;
  39.           -- Разблокируется иная сущность - пропустим действие
  40.           ELSE RAISE DO_NOTHING;
  41.         END CASE;
  42.       EXCEPTION
  43.         -- Если не смогли определить заявку - пропустим действие
  44.         WHEN NO_DATA_FOUND THEN RAISE DO_NOTHING;
  45.         -- Для других ошибок выбрасываем исключение
  46.         WHEN OTHERS THEN RAISE;
  47.       END;
  48.       -- Еслли не смогли определить заявку - пропустим действие
  49.       IF (v_case_rk IS NULL) THEN RAISE DO_NOTHING; END IF;
  50.  
  51.       /* Если снимается блокировка с инцидента или заявки, то
  52.         снимаем блокировку того же пользователя со всех связанных инцидентов */
  53.       BEGIN
  54.         SELECT INCIDENT_RK
  55.         -- Запомним снимаемые блокировки с инцидентов в списке
  56.         BULK COLLECT INTO g_i_delete_list
  57.         FROM CASEMGMT.INCIDENT_LIVE
  58.         WHERE
  59.           CASE_RK = v_case_rk
  60.           -- Исключим текущий инцидент, т.к. блокировка с него и так сейчас снимается
  61.           AND INCIDENT_RK <> CASE :old.ENTITY_NM WHEN 'INCIDENT' THEN :old.ENTITY_RK ELSE -1 END
  62.         ;
  63.       EXCEPTION
  64.         -- Если нет связанных блокировок, то продолжаем
  65.         WHEN NO_DATA_FOUND THEN NULL;
  66.         -- Для других ошибок выбрасываем исключение
  67.         WHEN OTHERS THEN RAISE;
  68.       END;
  69.  
  70.       -- Добавим заявку в список для снятия блокировок
  71.       IF (:old.ENTITY_NM = 'INCIDENT') THEN
  72.         g_c_delete_list.EXTEND; g_c_delete_list(g_c_delete_list.COUNT) := v_case_rk;
  73.       END IF;
  74.     -- Если блокировка устанавливается
  75.     ELSIF INSERTING THEN
  76.       -- запомним пользователя, кто блокирует заявку
  77.       g_lock_user_id := :NEW.LOCK_USER_ID;
  78.  
  79.       BEGIN
  80.         /* В зависимости от типа блокируемой сущности определим связанную заявку */
  81.         CASE :NEW.ENTITY_NM
  82.           -- Разблокируется заявка - сразу имеем её RK
  83.           WHEN 'CASE' THEN v_case_rk := :NEW.ENTITY_RK;
  84.           -- Разблокируется инцидент - запросим RK в INCIDENT_LIVE
  85.           WHEN 'INCIDENT' THEN
  86.             SELECT CASE_RK INTO v_case_rk FROM CASEMGMT.INCIDENT_LIVE WHERE INCIDENT_RK = :NEW.ENTITY_RK;
  87.           -- Разблокируется иная сущность - пропустим действие
  88.           ELSE RAISE DO_NOTHING;
  89.         END CASE;
  90.       EXCEPTION
  91.         -- Если не смогли определить заявку - пропустим действие
  92.         WHEN NO_DATA_FOUND THEN RAISE DO_NOTHING;
  93.         -- Для других ошибок выбрасываем исключение
  94.         WHEN OTHERS THEN RAISE;
  95.       END;
  96.       -- Если не смогли определить заявку - пропустим действие
  97.       IF (v_case_rk IS NULL) THEN RAISE DO_NOTHING; END IF;
  98.  
  99.       BEGIN
  100.         -- Выберем верификатора, которому назначена заявка
  101.         SELECT UPPER(INVESTIGATOR_USER_ID) INTO v_case_investigator FROM CASEMGMT.CASE_LIVE WHERE CASE_RK = v_case_rk;
  102.       EXCEPTION
  103.         -- Если верификатор не назначен - пропускаем ошибку
  104.         WHEN NO_DATA_FOUND THEN NULL;
  105.         -- Для других ошибок выбрасываем исключение
  106.         WHEN OTHERS THEN RAISE;
  107.       END;
  108.  
  109.       -- Проверим, можно ли конкретному пользователю блокировать заявку/инцидент
  110.       -- Если заявка не назначена пользователю, который пытается её заблокировать, то запретим такую блокировку
  111.       IF (UPPER(g_lock_user_id) <> NVL(v_case_investigator, '_foo_')) THEN
  112.         -- Снимем "несанкционированную" блокировку
  113.         CASE :NEW.ENTITY_NM
  114.           -- если блокируется заявка, добавим её RK в список для удаления
  115.           WHEN 'CASE'     THEN g_c_delete_list.EXTEND; g_c_delete_list(g_c_delete_list.COUNT) := :NEW.ENTITY_RK;
  116.           -- если блокируется инцидент, добавим его RK в список для удаления
  117.           WHEN 'INCIDENT' THEN g_i_delete_list.EXTEND; g_i_delete_list(g_i_delete_list.COUNT) := :NEW.ENTITY_RK;
  118.         END CASE;
  119.       -- Иначе (блокировка "санкционирована")
  120.       ELSE
  121.         -- Нужно заблокировать связанные инциденты (кроме инцидентов ТВ)
  122.         BEGIN
  123.           SELECT i.INCIDENT_RK
  124.           -- Запомним связанные инциденты в списке
  125.           BULK COLLECT INTO g_i_insert_list
  126.           FROM CASEMGMT.INCIDENT_LIVE i
  127.           -- посмотрим номер итерации инцидента
  128.           INNER JOIN CASEMGMT.INCIDENT_UDF_NUM_VALUE iItNo ON (
  129.             iItNo.INCIDENT_RK = i.INCIDENT_RK
  130.             AND iItNo.VALID_FROM_DTTM = i.VALID_FROM_DTTM
  131.             AND iItNo.UDF_TABLE_NM = 'INCIDENT'
  132.             AND iItNo.UDF_NM = 'X_ITERATION_NUM'
  133.             AND iItNo.ROW_NO = 1
  134.           )
  135.           -- посмотрим текущий номер итерации заявки
  136.           INNER JOIN CASE_LIVE c ON (c.CASE_RK = i.CASE_RK)
  137.           INNER JOIN CASEMGMT.CASE_UDF_NUM_VALUE cItNo ON (
  138.             cItNo.CASE_RK = c.CASE_RK
  139.             AND cItNo.VALID_FROM_DTTM = c.VALID_FROM_DTTM
  140.             AND cItNo.UDF_TABLE_NM = 'CASE'
  141.             AND cItNo.UDF_NM = iItNo.UDF_NM
  142.             AND cItNo.ROW_NO = 1
  143.           )
  144.           WHERE
  145.             i.CASE_RK = v_case_rk
  146.             -- исключим инциденты ТВ (они назначаются "каруселью" по одному)
  147.             AND i.INCIDENT_TYPE_CD NOT IN ('CV')
  148.             -- блокируем только связанные инциденты из текущей итерации заявки
  149.             AND cItNo.UDF_VALUE = iItNo.UDF_VALUE
  150.           ;
  151.         EXCEPTION
  152.           -- если нет связанных инцидентов, пропустим
  153.           WHEN NO_DATA_FOUND THEN NULL;
  154.           -- Для других ошибок выбрасываем исключение
  155.           WHEN OTHERS THEN RAISE;
  156.         END;
  157.  
  158.       END IF;
  159.  
  160.     END IF; /* DELETING|INSERTING */
  161.  
  162.   EXCEPTION
  163.     -- Если дана команда на пропуск секции, то ничего не делаем
  164.     WHEN DO_NOTHING THEN NULL;
  165.     -- Остальные исключения пробрасываем дальше
  166.     WHEN OTHERS THEN RAISE;  
  167.   END AFTER EACH ROW;
  168.  
  169.   -- Секция выполняется однократно по завершении выполнения конкретного INSERT или UPDATE
  170.   AFTER STATEMENT IS
  171.     -- временный список
  172.     v_temp_list NumberList_type := NumberList_type();
  173.   BEGIN
  174.     -- если надо заблокировать инциденты
  175.     IF (g_i_insert_list.COUNT > 0) THEN
  176.  
  177.       -- Пользователь может держать блокировки только по одной заявке,
  178.       BEGIN
  179.         -- очистим временный список
  180.         v_temp_list.DELETE;
  181.  
  182.         SELECT ENTITY_RK
  183.         -- запомним в списке блокировки, которые надо снять
  184.         BULK COLLECT INTO v_temp_list
  185.         FROM CASEMGMT.ECM_ENTITY_LOCK
  186.         WHERE
  187.           ENTITY_NM = 'INCIDENT'
  188.           -- всё, что этим пользователем не блокируется, должно быть разблокировано
  189.           AND ENTITY_RK NOT IN (SELECT column_value FROM TABLE(g_i_insert_list))
  190.           AND LOCK_USER_ID = g_lock_user_id
  191.         ;
  192.         -- Если есть посторонние блокировки
  193.         IF (v_temp_list.COUNT > 0) THEN
  194.           -- добавляем их в список на удаление
  195.           g_i_delete_list := g_i_delete_list MULTISET UNION v_temp_list;
  196.           -- очистим временный список
  197.           v_temp_list.DELETE;
  198.         END IF;
  199.       EXCEPTION
  200.         -- Если посторонних блокировок нет, пропустим этот шаг
  201.         WHEN NO_DATA_FOUND THEN NULL;
  202.         -- Остальные исключения пробрасываем дальше
  203.         WHEN OTHERS THEN RAISE;
  204.       END;
  205.  
  206.       -- Если снимаются блокировки с инцидентов, значит разблокируются и связанные с ними заявки
  207.       IF (g_i_delete_list.COUNT > 0) THEN
  208.         BEGIN
  209.           -- очистим временный список
  210.           v_temp_list.DELETE;
  211.  
  212.           SELECT CASE_RK AS val
  213.           -- запомним связанные заявки во временном списке
  214.           BULK COLLECT INTO v_temp_list
  215.           -- в таблицу блокировок сейчас не будем смотреть, т.к. её всё равно будет просматривать оператор DELETE
  216.           FROM CASEMGMT.INCIDENT_LIVE
  217.           WHERE INCIDENT_RK IN (SELECT column_value FROM TABLE(g_i_delete_list))
  218.           ;
  219.  
  220.           IF (v_temp_list.COUNT > 0) THEN
  221.             -- добавим связанные заявки в список на удаление блокировок
  222.             g_c_delete_list := g_c_delete_list MULTISET UNION v_temp_list;
  223.             -- очистим временный список
  224.             v_temp_list.DELETE;
  225.           END IF;
  226.         EXCEPTION
  227.           -- Если заявки не выбраны, пропустим этот шаг
  228.           -- (ситуация, правда, странная, но это проблема не для решения на уровне триггера)
  229.           WHEN NO_DATA_FOUND THEN NULL;
  230.           -- Остальные исключения пробрасываем дальше
  231.           WHEN OTHERS THEN RAISE;
  232.         END;
  233.       END IF;
  234.  
  235.       -- Вставим блокировки из списка на добавление
  236.       BEGIN
  237.         -- MERGE - для избегания попыток вставки блокировок, которые в таблице уже могут быть
  238.         MERGE INTO CASEMGMT.ECM_ENTITY_LOCK tgt
  239.         USING (
  240.           -- Обрабатываем список блокировок инцидентов
  241.           SELECT DISTINCT
  242.             column_value AS ENTITY_RK, 'INCIDENT' AS ENTITY_NM, g_lock_user_id AS LOCK_USER_ID, CURRENT_TIMESTAMP AS LOCK_DTTM
  243.           FROM TABLE(g_i_insert_list)
  244.         ) src
  245.         ON (tgt.ENTITY_RK = src.ENTITY_RK AND tgt.ENTITY_NM = src.ENTITY_NM)
  246.         -- Уберем возможные дубликаты
  247.         WHEN NOT MATCHED THEN INSERT (ENTITY_RK, ENTITY_NM, LOCK_USER_ID, LOCK_DTTM)
  248.         VALUES (src.ENTITY_RK, src.ENTITY_NM, src.LOCK_USER_ID, src.LOCK_DTTM)
  249.         ;
  250.       EXCEPTION
  251.         /* Если список блокировок не пуст, но ничего не выбралось, то пропустим этот шаг, т.к. иначе ЕСМ остановится;
  252.         могут копиться блокировки, но их администратор может снять вручную в административном поиске, либо они отвалятся по времени (ниже) */
  253.         WHEN NO_DATA_FOUND THEN NULL;
  254.         -- Остальные исключения пробрасываем дальше
  255.         WHEN OTHERS THEN RAISE;
  256.       END;
  257.       -- список обработали, очистим
  258.       g_i_insert_list.DELETE;
  259.     END IF;
  260.  
  261.     -- Если нужно разблокировать заявки или инциденты
  262.     IF (g_c_delete_list.COUNT > 0 OR g_i_delete_list.COUNT > 0) THEN
  263.       BEGIN
  264.         -- Удалим просрочку
  265.         DELETE FROM CASEMGMT.ECM_ENTITY_LOCK
  266.         WHERE ROWID IN (
  267.           SELECT t1.ROWID
  268.           -- Просмотрим блокировки инцидентов,
  269.           FROM CASEMGMT.ECM_ENTITY_LOCK t1
  270.           -- по которым пользователь не проявлял никакой активности более получаса
  271.           LEFT JOIN CASEMGMT.ECM_EVENT t2 ON (
  272.             t2.CREATE_DTTM >= CURRENT_TIMESTAMP - INTERVAL '30' MINUTE
  273.             AND t2.BUSINESS_OBJECT_NM = t1.ENTITY_NM
  274.             AND t2.BUSINESS_OBJECT_RK = t1.ENTITY_RK
  275.           )
  276.           WHERE
  277.             t1.LOCK_DTTM <= CURRENT_TIMESTAMP - INTERVAL '30' MINUTE
  278.             AND t1.ENTITY_NM IN ('INCIDENT')
  279.             AND t2.BUSINESS_OBJECT_RK IS NULL
  280.         );
  281.       EXCEPTION
  282.         -- Если нет просроченных заявок
  283.         WHEN NO_DATA_FOUND THEN
  284.           BEGIN
  285.             -- всё равно удалим блокировки, которые длятся явно дольше рабочего дня
  286.             DELETE FROM CASEMGMT.ECM_ENTITY_LOCK
  287.             WHERE ENTITY_NM IN ('CASE', 'INCIDENT')
  288.               AND LOCK_DTTM <= CURRENT_TIMESTAMP - INTERVAL '1' DAY
  289.             ;
  290.           EXCEPTION
  291.             -- если и таковых нет, пропустим шаг
  292.             WHEN NO_DATA_FOUND THEN NULL;
  293.             -- Остальные исключения пробрасываем дальше
  294.             WHEN OTHERS THEN RAISE;
  295.           END;
  296.         -- Остальные исключения пробрасываем дальше
  297.         WHEN OTHERS THEN RAISE;
  298.       END;
  299.  
  300.       -- Обработаем список удалемых блокировок инцидентов
  301.       IF (g_i_delete_list.COUNT > 0) THEN
  302.         BEGIN
  303.           DELETE FROM CASEMGMT.ECM_ENTITY_LOCK t1
  304.           WHERE
  305.             t1.ENTITY_NM = 'INCIDENT' AND t1.LOCK_USER_ID = g_lock_user_id
  306.             AND t1.ENTITY_RK IN (SELECT column_value FROM TABLE(g_i_delete_list))
  307.           ;
  308.         EXCEPTION
  309.           -- если блокировок уже нет, пропустим шаг
  310.           WHEN NO_DATA_FOUND THEN NULL;
  311.           -- Остальные исключения пробрасываем дальше
  312.           WHEN OTHERS THEN RAISE;
  313.         END;
  314.         -- список обработали, очистим
  315.         g_i_delete_list.DELETE;
  316.       END IF; -- g_i_delete_list
  317.  
  318.       -- Обработаем список удалемых блокировок заявок
  319.       IF (g_c_delete_list.COUNT > 0) THEN
  320.         BEGIN
  321.           DELETE FROM CASEMGMT.ECM_ENTITY_LOCK t1
  322.           WHERE
  323.             t1.ENTITY_NM = 'CASE' AND t1.LOCK_USER_ID = g_lock_user_id
  324.             AND t1.ENTITY_RK IN (SELECT column_value FROM TABLE(g_c_delete_list))
  325.           ;
  326.         EXCEPTION
  327.           -- если блокировок уже нет, пропустим шаг
  328.           WHEN NO_DATA_FOUND THEN NULL;
  329.           -- Остальные исключения пробрасываем дальше
  330.           WHEN OTHERS THEN RAISE;
  331.         END;
  332.         -- список обработали, очистим
  333.         g_c_delete_list.DELETE;
  334.       END IF; -- g_c_delete_list
  335.  
  336.     END IF;
  337.  
  338.   END AFTER STATEMENT;
  339.  
  340. END ECM_ENTITY_LOCK_TRIGGER;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement