
InterSystems Ensembleアプリケーション統合および開発プラットフォームに精通している人は、Ensembleワークフローサブシステムとは何か、それが人々の対話の自動化にどのように役立つかを知っています。 Ensemble(および/またはワークフロー)に慣れていない人のために、その機能について簡単に説明します(他の人はこの部分をスキップして、Angular.jsのワークフローユーザーインターフェイスの使用方法を見つけることができます)。
インターシステムズアンサンブル
統合とアプリケーション開発のプラットフォームInterSystems Ensembleは、異種システムを統合し、ビジネスプロセスを自動化し、統合アプリケーションの機能を新しいビジネスロジックまたはユーザーインターフェイスで補完する新しい複合アプリケーションを作成するように設計されています。 Ensembleは、EAI、SOA、BPM、BAM、さらにはBIまでの問題に対するソリューションを提供します(InterSystems DeepSeeの分析アプリケーションを開発するための組み込みテクノロジーによる)。
Ensembleの主なコンポーネントは次のとおりです。
- アダプタは、アプリケーション、テクノロジ、およびデータソースと対話するためのコンポーネントです。 Ensembleとともに、技術的および適用された統合アダプターが提供されます(WebおよびRestサービス、ファイル、FTP、電子メール、SQL、EDI、HL7、SAP、Siebel、1C Enterpriseなど)。 Adapter SDKを使用して独自のアダプターを作成できます。
- ビジネスサービスは、外部システムからのデータをEnsembleメッセージに変換し、ビジネスプロセスやビジネスオペレーションを呼び出すコンポーネントです。
- ビジネスプロセスは、システムおよび/または人の間の相互作用のシナリオを自動化するためにサービスと操作を調整するために使用される実行可能なプロセスです(ワークフローサブシステムを介して)。 プロセスは、ビジネスプロセス言語の宣言型言語で記述されるか、Cachéオブジェクトスクリプトで実装されます。 プロセスと外界との相互作用のロジックは、サービスと操作を使用した相互作用の具体的な実装から分離されています。
- ビジネスオペレーション -呼び出し/メッセージを外部システムに渡し、Ensembleメッセージを外部システムへの転送に適した形式に変換するコンポーネント。
- メッセージ変換 -メッセージをある形式から別の形式に変換するためのEnsembleコンポーネント。 実装には、宣言型言語のデータ変換言語が使用されます。
- ビジネスルール -プログラミングなしで統合ソリューションの管理者が、プロセスで指定された決定ポイントでEnsembleビジネスプロセスの動作を変更できるようにします。
- ワークフロー管理 -Ensembleワークフローサブシステムは、ユーザー間のタスクの分散を自動化します。
- ビジネスメトリック -主要業績評価指標を収集および計算し、ダッシュボードとともに、ビジネスアクティビティを監視するためのソリューションを作成するために使用します(ビジネスアクティビティモニタリング、BAM)。

ワークフロー管理に戻り、Ensembleワークフローサブシステムの機能をさらに詳しく検討してみましょう。
ワークフロー管理とアンサンブルワークフロー
Workflow Management Coalition(www.WfMC.org)の定義によれば、「ワークフローとは、一連の手続きルールに従って、ドキュメント、情報、またはタスクが参加者間で転送されるビジネスプロセスの全体または一部の自動化です。 」
主要なワークフロー要素:
- ワークフロータスク-作業の「断片化」
- ワークフロー-タスクを完了するための手順規則
- ワークフローユーザー-ワークフロー管理システムでタスクを実行する人
- ワークフローロールは、特定の種類のタスクを実行するユーザーのグループです。
Ensembleワークフロー管理サブシステムを使用すると、次のことができます。
- Ensembleビジネスプロセスを使用してワークフロー管理を自動化する
- 作業配分を柔軟に構成する
- Ensembleに付属する専用のワークフローポータルを介してワークフロー管理サブシステムを操作
- ワークフロー管理サブシステムと統合ビジネスプロセスEnsembleの相互作用を整理する
- ビジネスアクティビティ監視サブシステム、Ensemble管理および監視ユーティリティを使用する
- ワークフローサブシステムの機能を簡単にカスタマイズおよび拡張する
ワークフロー管理の自動化の最も簡単な例は、Ensembleサンプル配信の一部であり、Ensdemoエリアにあるカスタマーサポートチームワークを自動化するEnsemble HelpDeskアプリケーションです。 Ensembleは問題レポートを受け取り、HelpDeskビジネスプロセスを起動します。
HelpDeskビジネスプロセスアルゴリズムフラグメントビジネスプロセスは、可能なアクション(「固定」または「無視」)と「コメント」フィールドを定義するEnsLib.Workflow.TaskRequestクラスのメッセージを使用して、Demo-Developmentロールのユーザーにタスクを送信します。 メッセージの本文には、エラーに関する情報と問題を報告するユーザーも含まれます。 その後、デモ開発ロールのユーザーのワークフローポータルに対応するタスクが表示されます。

最初は(これがTaskRequestメッセージで指定されていない場合)、タスクはどのユーザーにも関連付けられていません(ただし、ロールにのみ関連付けられている)ため、ユーザーは対応するボタンをクリックして承認する必要があります。 また、いつでも「Give up」ボタンをクリックしてタスクを中止できます。
その後、特定のタスクで使用可能なアクションを実行できます。 この場合、対応するフィールドにコメントを指定した後、「固定」ボタンをクリックします。 HelpDeskビジネスプロセスは、このイベントを処理し、Demo-Testingロールのユーザーに新しいメッセージを送信して、行われた修正をテストする必要があることを通知します。 「無視」ボタンをクリックすると、タスクは単に「問題なし」としてマークされ、処理は終了します。
この例からわかるように、Ensemble Workflowは、ユーザーワークフローを整理するためのシンプルで直感的なシステムです。 Ensemble Workflowサブシステムの詳細については、「
ワークフローの
定義」セクションのEnsembleのドキュメントを参照してください。
Ensemble Workflowサブシステムの機能は、InterSystems Ensembleの外部複合アプリケーションに簡単に拡張および統合できます。 例として、Angular.js + REST APIで開発された外部複合アプリケーションでのEnsemble Workflowユーザーインターフェイス機能の実装を検討してください。
Angular.jsのEnsembleワークフローインターフェイス。
ワークフローユーザーインターフェイスをAngular.jsで機能させるには、Ensembleサーバーに次のアプリケーションをインストールする必要があります。
インストールプロセスは、指定されたリポジトリのReadmeに記載されています。
現時点では、アプリケーションはEnsemble Workflowのすべての基本機能を実装しています。タスクのリストの表示、追加のフィールドとアクション、並べ替え、タスクの全文検索。 ユーザーはタスクを承認/拒否でき、タスクに関する詳細情報がモーダルウィンドウに表示されます。
また、近い将来、アプリケーションに領域を変更する機能を追加する予定です(現時点では、アプリケーションはインストールされている領域でのみ動作します)。
執筆時点でのアプリケーションは次のとおりです。

必要に応じて、その後インターフェースを変更するために、Twitter Bootstrapが使用されました。技術的な実装の詳細
UIでは、Angular.js jsフレームワーク、Twitter Bootstrap cssフレームワーク、jQuery jsライブラリ、およびFontAwesomeアイコンフォントのライブラリとフレームワークが使用されます。
アプリケーションには、4つのAngularサービス(RESTSrvc、SessionSrvc、UtilSrvc、およびWorklistSrvc)、3つのコントローラー(MainCtrl、TaskCtrl、TasksGridCtrl)、メインページ(index.csp)、2つのテンプレート(task.cspおよびtasks.csp)があります。
RESTSrvcサービスにはgetPromiseメソッドが1つしかなく、$ http Angular.jsサービスのラッパーです。 RESTSrvcの唯一の目的は、HTTP要求をサーバーに送信し、これらの要求のpromiseオブジェクトを返すことです。 残りのサービスはRESTSrvcを使用して要求を行い、それらの分離は基本的に機能します。
RESTSrvc.js'use strict'; function RESTSrvc($http, $q) { return { getPromise: function(config) { var deferred = $q.defer(); $http(config). success(function(data, status, headers, config) { deferred.resolve(data); }). error(function(data, status, headers, config) { deferred.reject(data, status, headers, config); }); return deferred.promise; } } };
SessionSrvc-セッションを閉じるための1つのメソッドのみが含まれます。 アプリケーションでの認証は、基本アクセス認証(http://en.wikipedia.org/wiki/Basic_access_authentication)を使用して実行されます。したがって、各リクエストのヘッダーに認証トークンがあるため、認証方法は必要ありません。
UtilSrvc-名前によるCookie値の取得、名前によるオブジェクトプロパティの値の取得などのヘルパーメソッドが含まれています。
WorklistSrvcは、タスクリストデータに関連するクエリを実行します。
MainCtrl-メインアプリケーションコントローラーは、ユーザー認証を担当します。
TasksGridCtrl-タスクとアクションのリストのテーブルを管理するコントローラー。 タスクリストテーブルを初期化し、タスクのリストと特定のタスクをロードするためのメソッドと、ユーザーアクションを処理するためのメソッド(ボタンを押す、テーブルを並べ替える、テーブル行を強調表示、フィルタリング)を含みます。
TaskCtrl-タスクに関する詳細情報を含むモーダルウィンドウコントローラー。 フィールドとユーザーアクションのリストを生成し、モーダルウィンドウのボタン押下も処理します。
app.js-すべてのアプリケーションモジュールを含むファイル。
app.js 'use strict'; var servicesModule = angular.module('servicesModule',[]); var controllersModule = angular.module('controllersModule', []); var app = angular.module('app', ['ngRoute', 'ngCookies', 'ui.bootstrap', 'servicesModule', 'controllersModule']); app.config([ '$routeProvider', function( $routeProvider ) { $routeProvider.when( '/tasks', {templateUrl: 'partials/tasks.csp'} ); $routeProvider.when( '/tasks/:id', {templateUrl: 'partials/task.csp', controller: 'TaskCtrl'} ); $routeProvider.otherwise( {redirectTo: '/tasks'} ); }]);
index.cspは、アプリケーションのメインページです。
index.csp <!doctype html> <html> <head> <title>Ensemble Workflow</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="css/font-awesome.min.css"> <link rel="stylesheet" type="text/css" href="css/bootstrap-theme.min.css"> <link rel="stylesheet" type="text/css" href="css/custom.css"> <script language="javascript"> </script> </head> <body ng-app="app" ng-controller="MainCtrl"> <nav class="navbar navbar-default navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">Ensemble Workflow</a> </div> <div class="navbar-left"> <button ng-cloak ng-disabled="page.loginState != 1 || page.loading" type="button" class="btn btn-default navbar-btn" ng-click="page.dataInit();">Refresh Worklist</button> </div> <div class="navbar-left"> <form role="search" class="navbar-form"> <div class="form-group form-inline"> <label for="search" class="sr-only">Search</label> <input ng-cloak ng-disabled="page.loginState != 1" type="text" class="form-control" placeholder="Search" id="search" ng-model="query"> </div> </form> </div> <div class="navbar-right"> <form role="form" class="navbar-form form-inline" ng-show="page.loginState != 1" ng-model="user" ng-submit="page.doLogin(user.Login, user.PasswordSetter); user='';" ng-cloak> <div class="form-group"> <input class="form-control uc-inline" ng-model="user.Login" placeholder="Username" ng-disabled="page.loading"> <input type="password" class="form-control uc-inline" ng-model="user.PasswordSetter" placeholder="Password" ng-disabled="page.loading"> <button type="submit" class="btn btn-default" ng-disabled="page.loading">Sign In</button> </div> </form> </div> <button ng-show="page.loginState == 1" type="button" ng-click="page.doExit();" class="btn navbar-btn btn-default pull-right" ng-cloak>Logout, <span class="label label-info" ng-bind="utils.readCookie('User')"></span> </button> </div> </nav> <div class="container-fluid"> <div style="height: 20px;"> <div ng-show="page.loading" class="progress-bar progress-bar-striped progress-condensed active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%" ng-cloak> Loading </div> </div> <div ng-controller="AlertController" ng-cloak> <alert title="Click to dismiss" ng-repeat="alert in page.alerts" type="{{alert.type}}" ng-click="page.closeAlert($index, alert)">{{alert.msg}}</alert> </div> <div ng-show="page.loginState != 1" class="attention" ng-cloak> <p>Please, Log In first.</p> </div> <div ng-view> </div> </div> </div> <script language="javascript" src="libs/angular.min.js"></script> <script language="javascript" src="libs/angular-route.min.js"></script> <script language="javascript" src="libs/angular-cookies.min.js"></script> <script language="javascript" src="libs/ui-bootstrap-custom-tpls-0.12.0.min.js"></script> <script language="javascript" src="libs/base64.js"></script> <script language="javascript" src="js/app.js"></script> <script language="javascript" src="js/services/RESTSrvc.js"></script> <script language="javascript" src="js/services/WorklistSrvc.js"></script> <script language="javascript" src="js/services/SessionSrvc.js"></script> <script language="javascript" src="js/services/UtilSrvc.js"></script> <script language="javascript" src="js/controllers/MainCtrl.js"></script> <script language="javascript" src="js/controllers/TaskCtrl.js"></script> <script language="javascript" src="js/controllers/TasksGridCtrl.js"></script> <script language="javascript" src="libs/jquery-1.11.2.min.js"></script> <script language="javascript" src="libs/bootstrap.min.js"></script> </body> </html>
tasks.csp-タスクリストテーブルテンプレート。
tasks.csp <div class="row-fluid"> <div class="span1"> </div> <div ng-hide="page.loginState != 1 || (page.loading && !page.totalCnt())" ng-controller="TasksGridCtrl"> <div class="panel panel-default top-buffer"> <table class="table-tasks" ng-class="page.grid.cssClass" id="tasksTable"> <caption class="text-left"> <b ng-bind="page.grid.caption"></b><b id="updateTime" ng-bind="page.grid.updateTime"></b> </caption> <thead style="cursor: pointer; vertical-align: middle;"> <tr> <th class="text-center">#</th> <th ng-repeat="column in page.grid.columns" class="text-center" ng-click="page.sort(column.property, page.isUp)"> <span ng-bind="column.name" style="padding-right: 4px;"></span> <i style="margin-top: 3px;" ng-class="page.sortIcon" ng-show="column.property == page.predicate"></i> <i style="color: #ccc; margin-top: 3px;" class="fa fa-sort pull-right" ng-show="column.property != page.predicate"></i> </th> <th class="text-center">Action</th> </tr> </thead> <tfoot> <tr> <td colspan="{{page.grid.columns.length + 2}}"> <p ng-hide="page.grid.items.length">There is no task(s) for current user.</p> <span ng-show="page.grid.items.length"> Showing {{page.totalCnt()}} of {{page.grid.items.length}} task(s). </span> </td> </tr> </tfoot> <tbody style="cursor: default;"> <tr ng-repeat="item in page.grid.items | orderBy:page.predicate:page.isUp | filter:query" ng-class="item.rowCss" > <td ng-bind="$index + 1" class="text-right"></td> <td ng-repeat="column in page.grid.columns" style="text-align: {{column.align}};" ng-click="page.select(item)"> <span class="label label-info" ng-show="$first && item.New">New</span> <span ng-hide="$first" ng-bind="utils.getPropertyValue(item, column.property)"></span> </td> <td class="text-center"> <div title="Accept task" class="button button-success fa fa-plus-circle" ng-click="page.accept(item.ID)" ng-show="!page.isAssigned(item)"></div> <div title="Details" class="button button-info fa fa-search" ng-click="page.modalOpen('lg', item.ID)" ng-show="page.isAssigned(item)"></div> <div title="Yield task" class="button button-danger fa fa-minus-circle" ng-click="page.yield(item.ID)" ng-show="page.isAssigned(item)"></div> </td> </tr> </tbody> </table> </div> </div> <div class="span1"> </div> </div> <br>
task.csp-モーダルウィンドウテンプレート。
task.csp <div class="modal-header"> <h3 class="modal-title">Task description</h3> </div> <div class="modal-body"> <div class="container-fluid"> <div class="row top-buffer"> <div class="col-xs-12 col-md-6"> <div class="form-group"> <label for="subject">Subject</label> <input id="subject" type="text" class="form-control task-info-input" ng-model="page.task.Task['%TaskStatus'].Request['%Subject'];" readonly> </div> </div> <div class="col-md-6"> <div class="form-group"> <label for="timeCreated">Time created</label> <input id="timeCreated" type="text" class="form-control task-info-input" ng-model="page.task.Task['%TaskStatus'].TimeCreated;" readonly> </div> </div> </div> <div class="row"> <div class="col-md-12"> <div class="form-group"> <label for="message">Message</label> <textarea id="message" class="form-control task-info-input" ng-model="page.task.Task['%TaskStatus'].Request['%Message'];" rows="3" readonly></textarea> </div> </div> </div> <div class="row"> <div class="col-md-6"> <div class="form-group"> <label for="role">Role</label> <input id="role" type="text" class="form-control task-info-input" ng-model="page.task.Task['%TaskStatus'].Role.Name;" readonly> </div> </div> <div class="col-md-3"> <div class="form-group"> <label for="assignedTo">Assigned to</label> <input id="assignedTo" type="text" class="form-control task-info-input" ng-model="page.task.Task['%TaskStatus'].AssignedTo;" readonly> </div> </div> <div class="col-md-3"> <div class="form-group"> <label for="priority">Priority</label> <input id="priority" type="text" class="form-control task-info-input" ng-model="page.task.Task['%Priority'];" readonly> </div> </div> </div> <div class="row" ng-show="page.formFields"> <div class="delimeter col-md-6 el-centered"> </div> </div> <div class="row" ng-repeat="formField in page.formFields"> <div class="col-md-12"> <div class="form-group"> <label for="form{{$index}}" ng-bind="formField"></label> <input id="form{{$index}}" type="text" class="form-control task-info-input" ng-model="page.formValues[formField]"> </div> </div> </div> </div> </div> <div class="modal-footer"> <button ng-repeat="action in page.actions" class="btn btn-primary top-buffer" ng-click="page.doAction(action)" ng-bind="action"></button> <button class="btn btn-success top-buffer" ng-click="page.doAction('$Save')">Save</button> <button class="btn btn-warning top-buffer" ng-click="page.cancel()">Cancel</button> </div>
また、REST APIをUIに使用することを禁止する人はいません。特に、非常に単純であるためです。
REST APIのURLをマップします <Routes> <Route Url="/logout" Method="GET" Call="Logout"/> <Route Url="/tasks" Method="GET" Call="GetTasks"/> <Route Url="/tasks/:id" Method="GET" Call="GetTask"/> <Route Url="/tasks/:id" Method="POST" Call="PostTask"/> <Route Url="/test" Method="GET" Call="Test"/> </Routes>
HelpDeskアプリケーションを実行する
テストサーバーでユーザーインターフェイス
を試すことができます。 ログイン:dev /パス:123