Инструменты пользователя

Инструменты сайта


sharixopen:knowledge:scenarios:st_request_duplicate

Это старая версия документа!


ST_REQUEST-дубликат: ожидание решения о закрытии

Область: ShariX Open. Обработчик: open_st_request_duplicate, тип ST_REQUEST (1), рабочий статус DUPLICATE (159). Опубликованная встроенная реализация изучена на 28.10.2026; Python побайтово совпадает с канонической генерацией из Blockly. Выбранная версия на сервере проверяется отдельно.

Назначение и границы

Обработчик получает Ticket, уже отмеченный как дубликат, и ждёт отдельного решения о закрытии. Он не ищет похожие заявки, не выбирает основную карточку, не переносит описание, комментарии или вложения и не объединяет доменные результаты. Наличие статуса DUPLICATE не доказывает сохранность содержания в другой заявке.

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

Каноническое имя — open_st_request_duplicate. Имя open_st_request_dublicate с исторической опечаткой присутствует только как legacy-запись каталога: builtin_available=false, blockly_editable=false. Оно не является второй исполняемой встроенной реализацией этого сценария; создавать ещё одну LDAP/XMPP-учётку на основании такой записи не требуется.

Проход и локальное ожидание (BPMN)

Схема BPMN

Загрузка схемы…

Исходник схемы
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:ns2="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:ns3="http://www.omg.org/spec/DD/20100524/DC" xmlns:ns4="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_st_duplicate_logic" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="duplicate_Process_duplicate" name="open_st_request_duplicate: ожидание внешнего решения" isExecutable="false">
    <bpmn:documentation>{"notes": ["Опубликованная встроенная реализация; не доказательство выбранной версии на сервере.", "Процесс isExecutable=false, никакой handler при генерации не исполняется.", "deferred_until — символ локального срока, не имя параметра окружения."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_st_request_duplicate.py", "repository": "sharix-open-handlers-examples", "sha256": "33f2729a9ca9b61d1f41fff06e391089860cb02fd8a2104574c0c3d5933731aa"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_st_request_duplicate.json", "repository": "sharix-open-handlers-examples", "sha256": "350cd8d1e1d1014205a933dcac3871d14b82bb691d4464bfa477a706843fa637"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "utils.py", "repository": "sharix-open-tickets", "sha256": "a5680205c6522e1d9a74d9d267fe9ee96faf6f06436b00877fc0a93d80234bf5"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "views/ticket_detail.py", "repository": "sharix-open-tickets", "sha256": "05d344b0fba14cf895400694d4a18f486c800fef08dd1acb43ea46909b9e7e59"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/views/handler_workflow.py", "repository": "sharix-open-tickets", "sha256": "f97c4b467a9a30749f5c4b86b39a833c430e57aaa1f030f70030d9b5c609d368"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}]}</bpmn:documentation>
    <bpmn:laneSet id="duplicate_Lanes_duplicate">
      <bpmn:lane id="duplicate_Lane_worker" name="Worker: один проход Python">
        <bpmn:flowNodeRef>duplicate_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_status</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_reason</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_defer</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_cleanup</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_end</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_defer_error</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_error</bpmn:flowNodeRef>
      </bpmn:lane>
      <bpmn:lane id="duplicate_Lane_queue" name="Планировщик: между проходами">
        <bpmn:flowNodeRef>duplicate_wait</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_timer</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_revision</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>duplicate_ready</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="duplicate_start" name="Ticket готов к проходу">
      <bpmn:outgoing>duplicate_Flow_0</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:exclusiveGateway id="duplicate_status" name="Статус DUPLICATE?">
      <bpmn:incoming>duplicate_Flow_0</bpmn:incoming>
      <bpmn:incoming>duplicate_Flow_12</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_1</bpmn:outgoing>
      <bpmn:outgoing>duplicate_Flow_2</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="duplicate_reason" name="Записать support_duplicate_closure">
      <bpmn:incoming>duplicate_Flow_1</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_4</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="duplicate_defer" name="Назначить повтор по настройке">
      <bpmn:documentation>ST_REQUEST_DUPLICATE_RECHECK_SECONDS; пустой аргумент reason использует сохранённую локальную причину.</bpmn:documentation>
      <bpmn:incoming>duplicate_Flow_4</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_5</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="duplicate_cleanup" name="Убрать Ticket и удалить комнату">
      <bpmn:incoming>duplicate_Flow_2</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_3</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="duplicate_end" name="Текущий владелец освобождён">
      <bpmn:incoming>duplicate_Flow_3</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:boundaryEvent id="duplicate_defer_error" name="Неожиданная ошибка" attachedToRef="duplicate_defer" cancelActivity="true">
      <bpmn:outgoing>duplicate_Flow_6</bpmn:outgoing>
      <bpmn:errorEventDefinition />
    </bpmn:boundaryEvent>
    <bpmn:serviceTask id="duplicate_error" name="processing_error + общий defer">
      <bpmn:documentation>DEFERRED_TICKET_RECHECK_SECONDS</bpmn:documentation>
      <bpmn:incoming>duplicate_Flow_6</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_7</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:eventBasedGateway id="duplicate_wait" name="Что наступило раньше?">
      <bpmn:incoming>duplicate_Flow_5</bpmn:incoming>
      <bpmn:incoming>duplicate_Flow_7</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_8</bpmn:outgoing>
      <bpmn:outgoing>duplicate_Flow_9</bpmn:outgoing>
    </bpmn:eventBasedGateway>
    <bpmn:intermediateCatchEvent id="duplicate_timer" name="Наступил срок defer">
      <bpmn:incoming>duplicate_Flow_8</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_10</bpmn:outgoing>
      <bpmn:timerEventDefinition>
        <bpmn:timeDate xsi:type="bpmn:tFormalExpression">deferred_until</bpmn:timeDate>
      </bpmn:timerEventDefinition>
    </bpmn:intermediateCatchEvent>
    <bpmn:intermediateCatchEvent id="duplicate_revision" name="Получена новая ревизия Ticket">
      <bpmn:incoming>duplicate_Flow_9</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_11</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:intermediateCatchEvent>
    <bpmn:serviceTask id="duplicate_ready" name="Снять defer, вернуть в ready FIFO">
      <bpmn:documentation>Дубликат ревизии не снимает defer; при новом статусе очистка в следующем проходе/по ticket.moved.</bpmn:documentation>
      <bpmn:incoming>duplicate_Flow_10</bpmn:incoming>
      <bpmn:incoming>duplicate_Flow_11</bpmn:incoming>
      <bpmn:outgoing>duplicate_Flow_12</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="duplicate_Flow_0" sourceRef="duplicate_start" targetRef="duplicate_status" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_1" sourceRef="duplicate_status" targetRef="duplicate_reason" name="Да" />
    <bpmn:sequenceFlow id="duplicate_Flow_2" sourceRef="duplicate_status" targetRef="duplicate_cleanup" name="Нет" />
    <bpmn:sequenceFlow id="duplicate_Flow_3" sourceRef="duplicate_cleanup" targetRef="duplicate_end" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_4" sourceRef="duplicate_reason" targetRef="duplicate_defer" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_5" sourceRef="duplicate_defer" targetRef="duplicate_wait" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_6" sourceRef="duplicate_defer_error" targetRef="duplicate_error" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_7" sourceRef="duplicate_error" targetRef="duplicate_wait" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_8" sourceRef="duplicate_wait" targetRef="duplicate_timer" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_9" sourceRef="duplicate_wait" targetRef="duplicate_revision" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_10" sourceRef="duplicate_timer" targetRef="duplicate_ready" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_11" sourceRef="duplicate_revision" targetRef="duplicate_ready" name="" />
    <bpmn:sequenceFlow id="duplicate_Flow_12" sourceRef="duplicate_ready" targetRef="duplicate_status" name="Следующий проход" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_st_duplicate_logic">
    <bpmn:participant id="duplicate_Pool_duplicate" name="open_st_request_duplicate: ожидание внешнего решения" processRef="duplicate_Process_duplicate" />
  </bpmn:collaboration>
  <ns2:BPMNDiagram id="Diagram_st_duplicate_logic">
    <ns2:BPMNPlane id="Plane_st_duplicate_logic" bpmnElement="Collaboration_st_duplicate_logic">
      <ns2:BPMNShape id="duplicate_DI_Pool_duplicate" bpmnElement="duplicate_Pool_duplicate" isHorizontal="true">
        <ns3:Bounds x="0.0" y="0.0" width="1690.0" height="770.0" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_Lane_worker" bpmnElement="duplicate_Lane_worker" isHorizontal="true">
        <ns3:Bounds x="30.0" y="0.0" width="1660.0" height="385.0" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_Lane_queue" bpmnElement="duplicate_Lane_queue" isHorizontal="true">
        <ns3:Bounds x="30.0" y="385.0" width="1660.0" height="385.0" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_start" bpmnElement="duplicate_start">
        <ns3:Bounds x="132.0" y="92.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_status" bpmnElement="duplicate_status">
        <ns3:Bounds x="365.0" y="85.0" width="50" height="50" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_reason" bpmnElement="duplicate_reason">
        <ns3:Bounds x="575.0" y="75.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_defer" bpmnElement="duplicate_defer">
        <ns3:Bounds x="835.0" y="75.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_cleanup" bpmnElement="duplicate_cleanup">
        <ns3:Bounds x="315.0" y="265.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_end" bpmnElement="duplicate_end">
        <ns3:Bounds x="632.0" y="282.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_defer_error" bpmnElement="duplicate_defer_error">
        <ns3:Bounds x="942.0" y="127.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_error" bpmnElement="duplicate_error">
        <ns3:Bounds x="1095.0" y="265.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_wait" bpmnElement="duplicate_wait">
        <ns3:Bounds x="885.0" y="515.0" width="50" height="50" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_timer" bpmnElement="duplicate_timer">
        <ns3:Bounds x="1152.0" y="452.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_revision" bpmnElement="duplicate_revision">
        <ns3:Bounds x="1152.0" y="652.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="duplicate_DI_ready" bpmnElement="duplicate_ready">
        <ns3:Bounds x="1355.0" y="505.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNEdge id="duplicate_DI_Flow_0" bpmnElement="duplicate_Flow_0">
        <ns4:waypoint x="168.0" y="110.0" />
        <ns4:waypoint x="365.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_1" bpmnElement="duplicate_Flow_1">
        <ns4:waypoint x="415.0" y="110.0" />
        <ns4:waypoint x="575.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_2" bpmnElement="duplicate_Flow_2">
        <ns4:waypoint x="390.0" y="135.0" />
        <ns4:waypoint x="390.0" y="265.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_3" bpmnElement="duplicate_Flow_3">
        <ns4:waypoint x="465.0" y="300.0" />
        <ns4:waypoint x="632.0" y="300.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_4" bpmnElement="duplicate_Flow_4">
        <ns4:waypoint x="725.0" y="110.0" />
        <ns4:waypoint x="835.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_5" bpmnElement="duplicate_Flow_5">
        <ns4:waypoint x="910.0" y="145.0" />
        <ns4:waypoint x="910.0" y="515.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_6" bpmnElement="duplicate_Flow_6">
        <ns4:waypoint x="978.0" y="145.0" />
        <ns4:waypoint x="1020.0" y="145.0" />
        <ns4:waypoint x="1170.0" y="145.0" />
        <ns4:waypoint x="1170.0" y="265.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_7" bpmnElement="duplicate_Flow_7">
        <ns4:waypoint x="1245.0" y="300.0" />
        <ns4:waypoint x="1290.0" y="300.0" />
        <ns4:waypoint x="1290.0" y="410.0" />
        <ns4:waypoint x="910.0" y="410.0" />
        <ns4:waypoint x="910.0" y="515.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_8" bpmnElement="duplicate_Flow_8">
        <ns4:waypoint x="935.0" y="540.0" />
        <ns4:waypoint x="1043.5" y="540.0" />
        <ns4:waypoint x="1043.5" y="470.0" />
        <ns4:waypoint x="1152.0" y="470.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_9" bpmnElement="duplicate_Flow_9">
        <ns4:waypoint x="935.0" y="540.0" />
        <ns4:waypoint x="1043.5" y="540.0" />
        <ns4:waypoint x="1043.5" y="670.0" />
        <ns4:waypoint x="1152.0" y="670.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_10" bpmnElement="duplicate_Flow_10">
        <ns4:waypoint x="1188.0" y="470.0" />
        <ns4:waypoint x="1271.5" y="470.0" />
        <ns4:waypoint x="1271.5" y="540.0" />
        <ns4:waypoint x="1355.0" y="540.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_11" bpmnElement="duplicate_Flow_11">
        <ns4:waypoint x="1188.0" y="670.0" />
        <ns4:waypoint x="1271.5" y="670.0" />
        <ns4:waypoint x="1271.5" y="540.0" />
        <ns4:waypoint x="1355.0" y="540.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="duplicate_DI_Flow_12" bpmnElement="duplicate_Flow_12">
        <ns4:waypoint x="1505.0" y="540.0" />
        <ns4:waypoint x="1650.0" y="540.0" />
        <ns4:waypoint x="1650.0" y="25.0" />
        <ns4:waypoint x="390.0" y="25.0" />
        <ns4:waypoint x="390.0" y="85.0" />
        <ns2:BPMNLabel>
          <ns3:Bounds x="1330.0" y="5.0" width="180" height="26" />
        </ns2:BPMNLabel>
      </ns2:BPMNEdge>
    </ns2:BPMNPlane>
  </ns2:BPMNDiagram>
</bpmn:definitions>

В своём рабочем статусе handler записывает локальную причину support_duplicate_closure и назначает повтор по ST_REQUEST_DUPLICATE_RECHECK_SECONDS. Пустой reason в Blockly-примитиве defer использует уже сохранённую причину. Это состояние runtime и запись журнала, а не комментарий в Ticket и не уведомление пользователю.

Worker после прохода освобождается. Когда срок deferred_until наступает либо получена новая authoritative revision, Ticket становится готов к повторной проверке; одинаковый снимок не сбрасывает ожидание. Фактический запуск зависит от свободного worker и соединения XMPP. Defer хранится в памяти; после рестарта состав очереди сверяется с backend заново.

При чужом статусе запрашивается удаление локального Ticket и комнаты. Одного возврата None недостаточно для удаления: runtime учитывает явный запрос очистки. Неожиданное исключение обрабатывается общим processing_error с defer по DEFERRED_TICKET_RECHECK_SECONDS; автоматическое бизнес-решение вместо пользователя не принимается.

Внешний пользователь и смена владельца (BPMN)

Схема BPMN

Загрузка схемы…

Исходник схемы
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_st_duplicate_external" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="person_Process_person" name="Пользователь с доступом к Ticket и правом смены статуса" isExecutable="false">
    <bpmn:documentation>{"notes": ["Опубликованная встроенная реализация; не доказательство выбранной версии на сервере.", "Процесс isExecutable=false, никакой handler при генерации не исполняется.", "deferred_until — символ локального срока, не имя параметра окружения.", "Изменение статуса и best-effort XMPP доставка — отдельные операции. При потере события есть periodic reconciliation."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_st_request_duplicate.py", "repository": "sharix-open-handlers-examples", "sha256": "33f2729a9ca9b61d1f41fff06e391089860cb02fd8a2104574c0c3d5933731aa"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_st_request_duplicate.json", "repository": "sharix-open-handlers-examples", "sha256": "350cd8d1e1d1014205a933dcac3871d14b82bb691d4464bfa477a706843fa637"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "utils.py", "repository": "sharix-open-tickets", "sha256": "a5680205c6522e1d9a74d9d267fe9ee96faf6f06436b00877fc0a93d80234bf5"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "views/ticket_detail.py", "repository": "sharix-open-tickets", "sha256": "05d344b0fba14cf895400694d4a18f486c800fef08dd1acb43ea46909b9e7e59"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/views/handler_workflow.py", "repository": "sharix-open-tickets", "sha256": "f97c4b467a9a30749f5c4b86b39a833c430e57aaa1f030f70030d9b5c609d368"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}]}</bpmn:documentation>
    <bpmn:laneSet id="person_Lanes_person">
      <bpmn:lane id="person_Lane_worker" name="Осмысленное решение по карточке">
        <bpmn:flowNodeRef>person_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>person_choose</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>person_send</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>person_end</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="person_start" name="Решение по обращению">
      <bpmn:outgoing>person_Flow_0</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:userTask id="person_choose" name="Выбрать CLOSED">
      <bpmn:incoming>person_Flow_0</bpmn:incoming>
      <bpmn:outgoing>person_Flow_1</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sendTask id="person_send" name="Отправить смену статуса">
      <bpmn:incoming>person_Flow_1</bpmn:incoming>
      <bpmn:outgoing>person_Flow_2</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:endEvent id="person_end" name="Действие отправлено">
      <bpmn:incoming>person_Flow_2</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="person_Flow_0" sourceRef="person_start" targetRef="person_choose" name="" />
    <bpmn:sequenceFlow id="person_Flow_1" sourceRef="person_choose" targetRef="person_send" name="" />
    <bpmn:sequenceFlow id="person_Flow_2" sourceRef="person_send" targetRef="person_end" name="" />
  </bpmn:process>
  <bpmn:process id="backend_Process_backend" name="Tickets backend: объектный доступ, переход и commit" isExecutable="false">
    <bpmn:documentation>{"notes": ["Опубликованная встроенная реализация; не доказательство выбранной версии на сервере.", "Процесс isExecutable=false, никакой handler при генерации не исполняется.", "deferred_until — символ локального срока, не имя параметра окружения.", "Изменение статуса и best-effort XMPP доставка — отдельные операции. При потере события есть periodic reconciliation."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_st_request_duplicate.py", "repository": "sharix-open-handlers-examples", "sha256": "33f2729a9ca9b61d1f41fff06e391089860cb02fd8a2104574c0c3d5933731aa"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_st_request_duplicate.json", "repository": "sharix-open-handlers-examples", "sha256": "350cd8d1e1d1014205a933dcac3871d14b82bb691d4464bfa477a706843fa637"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "utils.py", "repository": "sharix-open-tickets", "sha256": "a5680205c6522e1d9a74d9d267fe9ee96faf6f06436b00877fc0a93d80234bf5"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "views/ticket_detail.py", "repository": "sharix-open-tickets", "sha256": "05d344b0fba14cf895400694d4a18f486c800fef08dd1acb43ea46909b9e7e59"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/views/handler_workflow.py", "repository": "sharix-open-tickets", "sha256": "f97c4b467a9a30749f5c4b86b39a833c430e57aaa1f030f70030d9b5c609d368"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}]}</bpmn:documentation>
    <bpmn:laneSet id="backend_Lanes_backend">
      <bpmn:lane id="backend_Lane_worker" name="Сохранение состояния">
        <bpmn:flowNodeRef>backend_request</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_acl</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_allowed</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_commit</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_publish</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_end</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_reject</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="backend_request" name="Запрос из карточки/API">
      <bpmn:outgoing>backend_Flow_0</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:startEvent>
    <bpmn:serviceTask id="backend_acl" name="Проверить права и переход lifecycle">
      <bpmn:incoming>backend_Flow_0</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_1</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="backend_allowed" name="Разрешено?">
      <bpmn:incoming>backend_Flow_1</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_2</bpmn:outgoing>
      <bpmn:outgoing>backend_Flow_3</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="backend_commit" name="Сохранить статус и Event">
      <bpmn:incoming>backend_Flow_2</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_4</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sendTask id="backend_publish" name="После commit: смена владельца">
      <bpmn:incoming>backend_Flow_4</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_5</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:endEvent id="backend_end" name="CLOSED сохранён; отдельный handler не запускается">
      <bpmn:incoming>backend_Flow_5</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:endEvent id="backend_reject" name="Отказ: переход не выполнен">
      <bpmn:incoming>backend_Flow_3</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="backend_Flow_0" sourceRef="backend_request" targetRef="backend_acl" name="" />
    <bpmn:sequenceFlow id="backend_Flow_1" sourceRef="backend_acl" targetRef="backend_allowed" name="" />
    <bpmn:sequenceFlow id="backend_Flow_2" sourceRef="backend_allowed" targetRef="backend_commit" name="Да" />
    <bpmn:sequenceFlow id="backend_Flow_3" sourceRef="backend_allowed" targetRef="backend_reject" name="Нет" />
    <bpmn:sequenceFlow id="backend_Flow_4" sourceRef="backend_commit" targetRef="backend_publish" name="" />
    <bpmn:sequenceFlow id="backend_Flow_5" sourceRef="backend_publish" targetRef="backend_end" name="" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_st_duplicate_external">
    <bpmn:participant id="person_Pool_person" name="Пользователь с доступом к Ticket и правом смены статуса" processRef="person_Process_person" />
    <bpmn:participant id="backend_Pool_backend" name="Tickets backend: объектный доступ, переход и commit" processRef="backend_Process_backend" />
    <bpmn:participant id="old_Participant" name="open_st_request_duplicate: исключить локальный снимок и комнату" />
    <bpmn:messageFlow id="Message_st_duplicate_external_0" sourceRef="person_send" targetRef="backend_request" name="Запрос перехода" />
    <bpmn:messageFlow id="Message_st_duplicate_external_1" sourceRef="backend_publish" targetRef="old_Participant" name="ticket.moved: прежний DUPLICATE" />
  </bpmn:collaboration>
  <bpmndi:BPMNDiagram id="Diagram_st_duplicate_external">
    <bpmndi:BPMNPlane id="Plane_st_duplicate_external" bpmnElement="Collaboration_st_duplicate_external">
      <bpmndi:BPMNShape id="person_DI_Pool_person" bpmnElement="person_Pool_person" isHorizontal="true">
        <dc:Bounds x="0.0" y="0.0" width="1100" height="200" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_Lane_worker" bpmnElement="person_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="0.0" width="1070" height="200" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_start" bpmnElement="person_start">
        <dc:Bounds x="132.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_choose" bpmnElement="person_choose">
        <dc:Bounds x="345.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_send" bpmnElement="person_send">
        <dc:Bounds x="625.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_end" bpmnElement="person_end">
        <dc:Bounds x="922.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="person_DI_Flow_0" bpmnElement="person_Flow_0">
        <di:waypoint x="168.0" y="100.0" />
        <di:waypoint x="345.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="person_DI_Flow_1" bpmnElement="person_Flow_1">
        <di:waypoint x="495.0" y="100.0" />
        <di:waypoint x="625.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="person_DI_Flow_2" bpmnElement="person_Flow_2">
        <di:waypoint x="775.0" y="100.0" />
        <di:waypoint x="922.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="backend_DI_Pool_backend" bpmnElement="backend_Pool_backend" isHorizontal="true">
        <dc:Bounds x="0.0" y="270.0" width="1630" height="380" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_Lane_worker" bpmnElement="backend_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="270.0" width="1600" height="380" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_request" bpmnElement="backend_request">
        <dc:Bounds x="132.0" y="352.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_acl" bpmnElement="backend_acl">
        <dc:Bounds x="345.0" y="335.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_allowed" bpmnElement="backend_allowed">
        <dc:Bounds x="675.0" y="345.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_commit" bpmnElement="backend_commit">
        <dc:Bounds x="875.0" y="335.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_publish" bpmnElement="backend_publish">
        <dc:Bounds x="1145.0" y="335.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_end" bpmnElement="backend_end">
        <dc:Bounds x="1452.0" y="352.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_reject" bpmnElement="backend_reject">
        <dc:Bounds x="682.0" y="532.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="backend_DI_Flow_0" bpmnElement="backend_Flow_0">
        <di:waypoint x="168.0" y="370.0" />
        <di:waypoint x="345.0" y="370.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_1" bpmnElement="backend_Flow_1">
        <di:waypoint x="495.0" y="370.0" />
        <di:waypoint x="675.0" y="370.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_2" bpmnElement="backend_Flow_2">
        <di:waypoint x="725.0" y="370.0" />
        <di:waypoint x="875.0" y="370.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_3" bpmnElement="backend_Flow_3">
        <di:waypoint x="700.0" y="395.0" />
        <di:waypoint x="700.0" y="532.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_4" bpmnElement="backend_Flow_4">
        <di:waypoint x="1025.0" y="370.0" />
        <di:waypoint x="1145.0" y="370.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_5" bpmnElement="backend_Flow_5">
        <di:waypoint x="1295.0" y="370.0" />
        <di:waypoint x="1452.0" y="370.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="DI_old_Participant" bpmnElement="old_Participant" isHorizontal="true">
        <dc:Bounds x="0" y="800" width="760" height="100" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="DI_Message_st_duplicate_external_0" bpmnElement="Message_st_duplicate_external_0">
        <di:waypoint x="700" y="135" />
        <di:waypoint x="700" y="225" />
        <di:waypoint x="150" y="225" />
        <di:waypoint x="150" y="352" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="250" y="195" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_st_duplicate_external_1" bpmnElement="Message_st_duplicate_external_1">
        <di:waypoint x="1220" y="405" />
        <di:waypoint x="1220" y="630" />
        <di:waypoint x="380" y="630" />
        <di:waypoint x="380" y="800" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="230" y="680" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

Пользователь должен иметь доступ к конкретному Ticket и право смены статуса. Авторство само по себе такого права не даёт. Канонические view/API проверяют объектный доступ и разрешённый переход lifecycle до сохранения; связанные с заказом Ticket не должны обходить жизненный цикл заказа через общую форму.

Из DUPLICATE текущий lifecycle разрешает только CLOSED. Это закрытие существующего Ticket, а не удаление записи или перенос её содержимого. Прямого перехода в REOPENED из этой стадии в изученном lifecycle нет; схема его не предлагает.

После commit backend публикует события изменения владельца. ticket.moved направляется прежнему handler и исключает локальный снимок; если worker уже выполняется, применяется pending discard до его завершения. Для новой стадии с зарегистрированным handler предназначен ticket.changed. Для CLOSED отдельного open_st_request_closed в каталоге нет.

XMPP-доставка best effort и не является частью транзакции сохранения Ticket. Потерю сообщения восстанавливает scoped periodic reconciliation по INTERVAL_TIME_BD_REQ. Успешный список без Ticket позволяет убрать прежний снимок; ошибка списка не должна трактоваться как пустая очередь. Новый локальный проход не заменяет проверку актуального backend-состояния.

Настройки и диагностика

Именованный интервал — ST_REQUEST_DUPLICATE_RECHECK_SECONDS, минимум одна секунда; реальное значение конкретного сервера здесь не предполагается. Обработчик не содержит фиксированного срока автоматического закрытия. Для общего восстановления используется DEFERRED_TICKET_RECHECK_SECONDS, для сверки состава — INTERVAL_TIME_BD_REQ.

При долгом ожидании проверьте актуальный статус и ревизию Ticket, выбранную версию Python/workspace, настройку интервала, причину ожидания, наличие доступного внешнего действия и права пользователя. Затем — scoped API, ready/defer, XMPP и periodic reconciliation. Не считайте штатное ожидание внешнего решения зависанием только по тому, что Ticket не поменялся от очередного таймера.

Проверенные источники

Алгоритм извлечён узким адаптером известной структуры Blockly и сверен реальным renderer с Python. Неизвестная структура или изменение изученных runtime/ACL-источников блокируют такую генерацию до повторного анализа. Это авторский инструмент для двух известных observer-сценариев, не общий автоматический hook обновления Wiki.

Связанные материалы

sharixopen/knowledge/scenarios/st_request_duplicate.1793223998.txt.gz · Последнее изменение: — sx_knowledge_api_open

© 2022 ShariX