2014-02-06

Программирование в бизнес-процессах в DocsVision

Бизнес-процессы - последовательность действий направленных на достижение некоторой заданной цели. Внешне представляются в виде блок-схем.
Рассмотрим возможности программирования в бизнес-процессах в DocsVision.
Задача примерно следующая:
1) Мониторинг новых и измененных документов в состоянии "Опубликовано".
2) Выгрузка найденных документов в текстовые файлы на диск.
Суть, кратко: найти карточки по фильтру и затем что-то с ними сделать.

Разработка бизнес-процесса

Подготовим зацикленную схему для постоянного мониторинга карточек. В настройках ограничим запуск одним экземпляром.
Рис. 1. Блок-схема бизнес-процесса

Создадим необходимые переменные.
В last_execute будем записывать время последнего запуска скрипта, оно нам понадобится, чтобы найти созданные и измененные документы с момента последнего запуска.
В changed_docs будем записывать результаты мониторинга, то есть все найденные по условию карточки.
Рис. 2. Переменные бизнес-процесса

Начальная функция

Точка входа в бизнес-процесс.

Мониторинг DocsVision

Позволяет искать карточки по фильтру или мониторить изменения отдельно взятой карточки, а именно, ее секций, строк и полей. Для фильтра используется поиск и запросы создаются в конструкторе.
В настройках зададим очиску истории, переменную для хранения результата, настроим фильтр и создадим параметр для него.
Рис. 3. Свойства мониторинга

По запросу будем искать все в состоянии "Опубликованные", с актуальной датой документа (из динамического поля) и датой изменения карточки более либо равной даты из параметра-переменной, в который будем периодически заносить время последнего запуска скрипта.
Рис. 4. Атрибутивный поиск

Связываем параметр поиска с переменной.
Рис. 5. Параметры для фильтра

Сценарий

Самый интересный блок для программиста. Переменные на входе, переменные на выходе и полная свобода кодинга.
В планах использовать не только шлюзы DocsVision  (DVGate), но объекты DocsVision. Для этого подключаем необходимые сборки, как минимум, DocsVision.Platform.StorageServer, DocsVision.Platform.ObjectModel и DocsVision.BackOffice.ObjectModel, а другие по мере востребованности.

// подключение системных библиотек
using System;
using System.Xml;

// подключение библиотек СУБП
using DocsVision.Workflow.Objects;
using DocsVision.Workflow.Runtime;
using DocsVision.Workflow.Gates;
using DocsVision.Platform.HelperAPI;

// прочие сборки DocsVision
using DocsVision.Platform.ObjectModel;
using DocsVision.Platform.ObjectModel.Mapping;
using DocsVision.Platform.ObjectModel.Persistence;
using DocsVision.Platform.ObjectModel.Search;
using DocsVision.Platform.Data.Metadata;
using DocsVision.BackOffice.ObjectModel;
using DocsVision.BackOffice.ObjectModel.Mapping;
using DocsVision.BackOffice.ObjectModel.Services;
using System.ComponentModel.Design;
using System.Collections.Generic;

namespace DVScriptHost
{
 class DVScript
 {
  
  public void Execute(ProcessInfo process, PassState passInfo)
  {
   try
   {
    // получаем контекст
    ObjectContext objectContext = CreateContext(process);
    
    DVGate dvGate = (DVGate)process.Gates[DVGate.GateID];
    
    List<String> ids = new List<String>();
    
    // записываем идентификаторы найденных карточек в список
    ProcessVariable changed_docs = process.GetVariableByName("changed_docs");
    ProcessVariableValues docs = changed_docs.Values;
    foreach (ProcessVariableValue val in docs.Values)
    {
     DVCard dvc = (DVCard)val.Value;
     ids.Add(dvc.ID);
    }
    
    if (ids.Count > 0)
    {
     // делаем запись в лог бизнес-процесса
     process.LogMessage("Измененные: " + String.Join<String>(" / ", ids));
     // тут я использую заранее разработанное серверное расширение для записи, но можно обойтись и без него так как код бизнес-процесса выполняется на серверной стороне
     // получаем метод из серверного расширения
     DocsVision.Platform.ObjectManager.ExtensionMethod mymethod = dvGate.UserSession.ExtensionManager.GetExtensionMethod("LibraryServerExtension", "ExportList");
     // передаем в серверное расширение необходимые параметры
     mymethod.Parameters.AddNew("ConnectionString", DocsVision.Platform.ObjectManager.ParameterValueType.String, dvGate.UserSession.Connection.ConnectionString);
     mymethod.Parameters.AddNew("FolderId", DocsVision.Platform.ObjectManager.ParameterValueType.Guid, Guid.Empty);
     mymethod.Parameters.AddNew("ListDocId", DocsVision.Platform.ObjectManager.ParameterValueType.String, String.Join<String>(",", ids));
     String result = mymethod.Execute().ToString();
     // пишем в лог финальные результат
     process.LogMessage("Выгрузка: " + result.Replace(",", " / "));
    }
   }
   catch (Exception ex)
   {
    // запись в журнал ошибки исполнения
    process.LogMessage("Ошибка выполнения скрипта: " + ex.Message);
   }
   return;
  }
  
  // код создания контекста
  private static ObjectContext CreateContext(ProcessInfo process)
  {
   DVGate dvGate = (DVGate)process.Gates[DVGate.GateID];
   
   ServiceContainer sessionContainer = new ServiceContainer();
   sessionContainer.AddService(
    Type.GetType("DocsVision.Platform.ObjectManager.UserSession, DocsVision.Platform.ObjectManager, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7148afe997f90519"),
    dvGate.UserSession
   );
   
   ObjectContext objectContext = new ObjectContext(sessionContainer);
   IObjectMapperFactoryRegistry mapperFactoryRegistry = objectContext.GetService<IObjectMapperFactoryRegistry>();
   mapperFactoryRegistry.RegisterFactory(typeof(BackOfficeMapperFactory));
   IServiceFactoryRegistry serviceFactoryRegistry = objectContext.GetService<IServiceFactoryRegistry>();
   serviceFactoryRegistry.RegisterFactory(typeof(BackOfficeServiceFactory));
   objectContext.AddService(DocsVisionObjectFactory.CreatePersistentStore(new SessionProvider(dvGate.UserSession), null));
   IMetadataProvider metadataProvider = DocsVisionObjectFactory.CreateMetadataProvider(dvGate.UserSession);
   objectContext.AddService(DocsVisionObjectFactory.CreateMetadataManager(metadataProvider, dvGate.UserSession));
   objectContext.AddService(metadataProvider);
   return objectContext;
  }
  
 }
}

Универсальная функция

Позволяет производить не сложные манипуляции над переменными бизнес-процесса.
В данной задаче блок имеет следующее назначение: записать в переменную last_execute текущую дату/время.
Рис. 6. Свойства блока "Универсальная функция"

Запуск и жизненный цикл

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