搜索​​​​

清除过滤器
问题
li wang · 四月 23, 2022

浏览器地址中输入HealtConnect发布的webService地址报CSP应用程序错误

各位老师,你们好 我在浏览器地址中输入HealtConnect发布的webService地址报 CSP应用程序错误,并记录了此错误,不过该地址还是能正常访问。 我想知道怎么解决这种报错问题,谢谢! 以下图片为HealthConnect的版本,以及具体出现的错误 建议联系我们WRC团队support@intersystems.com 或者电话:4006019890或者对应的客户经理,谢谢! 应该是没开启SOAP测试页能力。搜一下三级等保文章,里面有介绍 https://cn.community.intersystems.com/smartsearch?search=InterSystems+数据平台与三级等保+-+第一篇
文章
Weiwei Gu · 八月 14, 2023

调试管理门户加载图片失败的原因

在提交的 WRC case中(Intersystems 全球技术支持响应中心),我们经常看到客户提出有关新 Web 网关设置的问题,其中管理门户加载一半,但不显示图像。本文将解释为什么会出现此错误,以及如何修复它。本说明主要针对服务 InterSystems IRIS 实例的 Web 网关,但相同的说明也应适用于服务 Caché 实例的 CSP 网关。 问题: 您刚刚在独立的 Web 服务器上安装了 Web Gateway。当你去加载管理门户时,你发现它无法显示或加载图像,如下所示: 为什么会发生这种情况: 问题是,为了完整加载管理门户,InterSystems IRIS 必须加载许多 .js、.css 和 .png 文件(静态文件)。如果您看到像上面这样的管理门户页面,请随时打开浏览器的开发人员工具小程序,导航到“网络”选项卡,并确认未提供各种 .js、.css 和 .png 组件: 最初安装 Web Gateway 时,我们仅为以下扩展设置映射: .csp .cls .zen .cxw 这些是客户在自己的自定义应用程序中最常使用的文件扩展名类型,以及用于为 Web Gateway 管理门户提供服务的 .cxw 扩展名。如果您想要加载其他管理门户组件,则必须注册其他文件类型以由 Web 网关提供服务。 如何解决该问题: 要使管理门户完全显示,您必须配置 Web 网关以提供其他文件类型。对于 IIS,您可以为 .js、.png、.css 等扩展名添加单独的映射,也可以添加通配符映射。可以在此处找到有关注册 IIS 的其他文件类型的文档:https: //docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls ?KEY=GCGI_win#GCGI_registering 如果您在 Unix / Linux 系统上的 Apache 之上运行 Web Gateway,您有几个选择。您可以通过添加其他文件扩展名(如 IIS 上的情况)来配置此功能,也可以添加 CSP 位置指令。请参阅此处的文档了解完整详细信息: https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls ?KEY=GCGI_ux,位于“使用 CSP 注册其他文件类型”下
文章
Hao Ma · 一月 15, 2021

IAM实践指南——OAuth 2.0下的API保卫战(第一部分)

介绍 目前,诸多应用程序通过开放授权框架(OAuth)来安全、可靠、高效地访问各种服务中的资源。InterSystems IRIS目前已兼容OAuth 2.0框架。事实上社区有一篇关于OAuth 2.0和InterSystems IRIS的精彩文章,链接如下。 然而,随着API管理工具的出现,一些组织开始将其用作单点身份验证,从而防止未经授权的请求到达下游服务,并将授权/身份验证复杂性从服务本身分离出来。 您可能知道,InterSystems已经推出了自己的API管理工具,即InterSystems API Management (IAM),以IRIS Enterprise license(IRIS Community版本不含此功能)的形式提供。这里是社区另一篇介绍InterSystems AIM的精华帖。 这是三篇系列文章中的第一篇,该系列文章将展示如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。 第一部分将介绍OAuth 2.0相关背景,以及IRIS和IAM的初始定义和配置,以帮助读者理解确保服务安全的整个过程。 本系列文章的后续部分还将介绍两种使用IAM保护服务的可能的场景。在第一种场景中,IAM只验证传入请求中的访问令牌,如果验证成功,则将请求转发到后端。在第二种场景中,IAM将生成一个访问令牌(充当授权服务器)并对其进行验证。 因此,第二篇将详细讨论和展示场景1中的配置步骤,第三篇将讨论和演示场景2中的配置以及一些最终要考虑的因素。 如果您想试用IAM,请联系InterSystems销售代表。 OAuth 2.0背景 每个OAuth 2.0授权流程基本上都由4个部分组成: 用户 客户端 授权服务器 资源所有者 简单起见,本文使用“资源所有者密码凭证”OAuth流(可以在IAM中使用任何OAuth流)。另外,本文将不指定任何使用范围。 注意:因为资源所有者密码凭证流直接处理用户凭证,所以应该只在客户端应用程序高度受信任时使用。在大多数情况下,客户端应为第一方应用程序。 通常,资源所有者密码凭证流遵循以下步骤: 用户在客户端应用程序中输入凭证(如用户名和密码) 客户端应用程序将用户凭证和自身的标识(如客户端ID和客户端密钥)一起发送到授权服务器。授权服务器验证用户凭证和客户端标识,并返回访问令牌 客户端使用令牌访问资源服务器上的资源 资源服务器首先验证收到的访问令牌,然后再将信息返回给客户端 考虑到这种情况,你可以在两种场景下使用IAM应对OAuth 2.0: IAM充当验证器,验证客户端应用程序提供的访问令牌,仅在访问令牌有效时才将请求转发给资源服务器;在这种情况下,访问令牌将由第三方授权服务器生成 IAM既充当授权服务器(向客户端提供访问令牌),又充当访问令牌验证器,在将请求重定向到资源服务器之前验证访问令牌。 IRIS和IAM初始定义和配置 本文中使用名为“/SampleService”的IRIS Web应用程序。从下面的截屏中可以看到,这是一个在IRIS中部署的未经身份验证的REST服务: 此外,在IAM端配置了一个名为“SampleIRISService”的服务,其包含一个路由,如以下截屏所示: 再者,在IAM中配置了一个名为“ClientApp”的客户端(初始没有任何凭据),用来识别谁在调用IAM中的API: 经上述配置,IAM将发送到以下URL的每个GET请求代理到IRIS: http://iamhost:8000/event 此时还没有使用身份验证。所以,如果将一个简单的GET请求(未进行身份验证)发送到URL: http://iamhost:8000/event/1 我们将获得期望的响应。 本文中,我们使用名为“PostMan”的应用程序发送请求并检查响应。在下面的PostMan截屏中,可以看到简单的GET请求及其响应。 请继续阅读本系列的第2篇,了解如何配置IAM来验证传入请求中的访问令牌。
文章
姚 鑫 · 九月 29, 2021

第二十九章 SQL命令 DISTINCT

# 第二十九章 SQL命令 DISTINCT 指定仅返回不同值的`SELECT`子句。 # 大纲 ```sql SELECT [DISTINCT [BY (item {,item2})] ] | [ALL] select-item {,select-item2} ``` ## 参数 - `DISTINCT` - 可选-返回组合选择项值唯一的行。 - `DISTINCT BY (item {,item2})` - 可选-返回按(项)值唯一的行的选择项值。 - `ALL` - 可选-返回结果集中的所有行。默认设置。 # 描述 可选`DISTINCT`子句出现在`SELECT`关键字之后、可选`TOP`子句和第一个`SELECT-ITEM`之前。 `DISTINCT`子句应用于`SELECT`语句的结果集。它将每个不同(唯一)值返回的行数限制为一个任意行。如果未指定`DISTINCT`子句,则默认情况下显示满足选择条件的所有行。`ALL`子句与不指定`DEFAULT`子句相同;如果指定`ALL`,`SELECT`将返回表中满足选择条件的所有行。 `DISTINCT`从句有两种形式: - `SELECT DISTINCT`:为选择项值的每个唯一组合返回一行。可以指定一个或多个选择项。例如,以下查询返回一行,其中包含`Home_State`和`Age`值的每个唯一组合的`Home_State`和`Age`值: ```sql SELECT DISTINCT Home_State,Age FROM Sample.Person ``` - `SELECT DISTINCT BY(Item)`:为项目值的每个唯一组合返回一行。可以指定单个项目或逗号分隔的项目列表。指定的项目或项目列表必须用括号括起来。可以在by关键字和圆括号之间指定或省略空格。选择项列表可以(但不一定)包括指定的项。例如,以下查询返回一行,其中包含`Home_State`和`Age`值的每个唯一组合的`Name`和`Age`值: ```sql SELECT DISTINCT BY (Home_State,Age) Name,Age FROM Sample.Person ``` 项目字段必须按列名指定。有效值包括以下值:列名(`DISTINCT BY(City)`);`%ID`(返回所有行);指定列名的标量函数(`DISTINCT BY(ROUND(Age,-1)`;指定列名的排序函数(`DISTINCT BY(%Exact(City)`。不能按列别名指定字段;尝试这样做会生成`SQLCODE-29`错误。不能按列号指定字段;这将被解释为文字,并返回一行。将文字指定为`DISTINCT`子句中的项值将返回`1`行;返回哪行是不确定的。因此,指定`7`、`‘Chicago’`、`‘’`、`0`或`NULL`都返回`1`行。但是,如果将文字指定为逗号分隔列表中的项值,则该文字将被忽略,并且`DISTINCT`将为指定字段名的每个唯一组合选择一行。 `DISTINCT`子句在`TOP`子句之前应用。如果两者都指定,则`SELECT`只返回具有唯一值的行,唯一值是在`TOP`子句中指定的唯一值行数。 如果`DISTINCT`子句中指定的列包含`NULL`(不包含值)行,则`DISTINCT`将返回一行作为`DISTINCT`(唯一)值的`NULL`,如以下示例所示: ```sql SELECT DISTINCT FavoriteColors FROM Sample.Person ``` ```sql SELECT DISTINCT BY (FavoriteColors) Name,FavoriteColors FROM Sample.Person ORDER BY FavoriteColors ``` `DISTINCT`子句在嵌入式SQL简单查询中没有意义,因为在这种类型的嵌入式SQL中,`SELECT`始终只返回一行数据。但是,嵌入式SQL基于游标的查询可以返回多行数据;在基于游标的查询中,`DISTINCT`子句只返回唯一值行。 ## DISTINCT和ORDER BY `DISTINCT`子句在`ORDER BY`子句之前应用。因此,`DISTINCT`和`ORDER BY`的组合将首先选择满足`DISTINCT`子句的任意行,然后根据`ORDER BY`子句对这些行进行排序。 ## DISTINCT和GROUP BY `DISTINCT`和`GROUP BY`这两个记录按指定字段(或多个字段)分组,并为该字段的每个唯一值返回一条记录。它们之间的一个重要区别是`DISTINCT`在分组之前计算聚合函数。`GROUP BY`计算分组后的聚合函数。以下示例显示了这种差异: ```sql SELECT DISTINCT BY (ROUND(Age,-1)) Age,AVG(Age) AS AvgAge FROM Sample.Person /* Avg(Age)返回表中所有年龄的平均值 */ ``` ```sql SELECT Age,AVG(Age) AS AvgAge FROM Sample.Person GROUP BY ROUND(Age,-1) /* Avg(Age)返回每个年龄组的平均年龄 */ ``` `DISTINCT`子句可以用一个或多个聚合函数字段指定,尽管这很少有意义,因为聚合函数返回单个值。因此,下面的示例返回单行: ```sql SELECT DISTINCT BY (AVG(Age)) Name,Age,AVG(Age) FROM Sample.Person ``` 注意:如果将聚合函数作为唯一项或选择项的`DISTINCT`子句与`GROUP BY`子句一起使用,则`DISTINCT`子句将被忽略。可以使用子查询实现`DISTINCT`、聚合函数和`GROUP BY`的预期组合。 ## 字母大小写与DISTINCT优化 根据为字段定义的排序规则类型,将字符串值不同地分组在一起。默认情况下,字符串数据类型字段使用`SQLUPPER`排序规则定义,该排序规则不区分大小写。 如果字段/特性排序规则类型为`SQLUPPER`,则分组的字段值将全部以大写字母返回。要按原始字母大小写对值进行分组,或以原始字母大小写显示分组字段的返回值,请使用`%Exact`排序规则函数。以下示例显示了这一点,这些示例假设`Home_City`字段是使用排序规则类型`SQLUPPER`定义的,并且包含值`‘New York’`和`‘New York’`: ```sql SELECT DISTINCT BY (Home_City) Name,Home_City FROM Sample.Person /* 将Home_City值按其大写字母值组合在一起将以大写字母返回每个分组城市的名称。因此,返回‘new york’. */ ``` ```sql SELECT DISTINCT BY (Home_City) Name,%EXACT(Home_City) FROM Sample.Person /* 将Home_City值按其大写字母值组合在一起将返回每个分组的城市的名称(原始字母大小写)。因此,可以返回‘New York’或‘new York’,但不能同时返回两者。 */ ``` ```sql SELECT DISTINCT BY (%EXACT(Home_City)) Name,Home_City FROM Sample.Person /* 将Home_City值按其原始字母大小写组合在一起将返回每个分组的城市的名称(原始字母大小写)。 因此,‘New York’和‘new York’都会返回。 未使用优化. */ ``` 可以使用管理门户优化包含`DISTINCT`子句的查询的查询性能。依次选择系统管理、配置、SQL和对象设置、SQL。查看和编辑`GROUP BY`和`DISTINCT`查询必须生成原始值选项。(此优化也适用于`GROUP BY`子句。)。默认值为“否”。 此默认设置按字母值的大写排序规则对字母值进行分组。此优化利用选定字段的索引。因此,只有在一个或多个选定字段存在索引时才有意义。它对存储在索引中的字段值进行排序;字母字符串以全部大写字母返回。您可以设置此系统范围的选项,然后使用`%exact`排序规则函数为特定查询覆盖它以保留字母大小写。 也可以使用`$SYSTEM.SQL.Util.SetOption()`方法快速区分选项在系统范围内设置此选项。要确定当前设置,请调用`$SYSTEM.SQL.CurrentSettings()`,它显示打开的不同优化设置;默认值为`1`。 ## DISTINCT的其他用法 - 流字段:`DISTINCT`对流字段的`OID`进行操作,而不是对其实际数据进行操作。因为所有流字段`OID`都是唯一值,所以`DISTINCT`对实际流字段重复数据值没有影响。`DISTINCT BY(StreamField)`将流字段为空的记录数减少到一个空记录。 - 星号语法:`DISTINCT*`语法是合法的,但没有意义,因为根据定义,所有行都包含一些不同的唯一标识符。不同于(`*`)的语法不合法。 - 子查询:在子查询中使用`DISTINCT`子句是合法的,但没有意义,因为子查询返回单个值。 - 未选择行数据:`DISTINCT`子句可以与不访问任何表数据的`SELECT`一起使用。如果`SELECT`包含`FROM`子句,则在一行中指定`DISTINCT`结果将包含这些非表值;如果未指定`DISTINCT`(或`TOP`),则`SELECT`将产生与`FROM`子句表中的行数相同的行数。如果`SELECT`不包含`FROM`子句,则`DISTINCT`是合法的,但没有意义。 - 聚合函数:可以在聚合函数中使用`DISTINCT`子句,以仅选择要包含在聚合中的不同(唯一)字段值。与`SELECT DISTINCT`子句不同,聚合函数中的`DISTINCT`不包括`NULL`作为`DISTINCT`(唯一)值。请注意,`MAX`和`MIN`聚合函数分析`DISTINCT`子句语法没有错误,但此语法不执行任何操作。 ## DISTINCT和%ROWID 指定`DISTINCT`关键字会导致基于游标的嵌入式SQL查询不设置`%ROWID`变量。即使`DISTINCT`不限制返回的行数,也不设置`%ROWID`。下面的示例显示了这一点: ```java ClassMethod Distinct() { s %ROWID = 999 &sql( DECLARE EmpCursor3 CURSOR FOR SELECT DISTINCT Name, Home_State INTO :name,:state FROM Sample.Person WHERE Home_State %STARTSWITH 'M' ) &sql( OPEN EmpCursor3 ) q:(SQLCODE '= 0) for { &sql( FETCH EmpCursor3 ) q:SQLCODE w !,"RowID: ",%ROWID," row count: ",%ROWCOUNT w " Name=",name," State=",state } &sql( CLOSE EmpCursor3 ) } ``` 查询行为的这种更改仅适用于基于游标的嵌入式`SQL SELECT`查询。动态`SQL SELECT`查询和非游标嵌入式`SQL SELECT`查询从未设置`%ROWID`。 ## DISTINCT和事务处理 指定`DISTINCT`关键字会导致查询检索所有当前数据,包括当前事务尚未提交的数据。忽略事务的`READ COMMITTED`隔离模式参数(如果设置);在`READ UNCOMMITTED`模式下检索所有数据。 # 示例 以下查询为每个不同的`Home_State`值返回一行: ```sql SELECT DISTINCT Home_State FROM Sample.Person ORDER BY Home_State ``` 以下查询为每个不同的`Home_State`值返回一行,但返回该行的其他字段。无法预测检索到的是哪一行: ```sql SELECT DISTINCT BY (Home_State) %ID,Name,Home_State,Office_State FROM Sample.Person ORDER BY Home_State ``` 以下查询为`Home_State`和`Office_State`值的每个不同组合返回一行。根据数据的不同,它要么返回更多行,要么返回与上一个示例相同的行数: ```sql SELECT DISTINCT BY (Home_State,Office_State) %ID,Name,Home_State,Office_State FROM Sample.Person ORDER BY Home_State,Office_State ``` 以下查询使用`DISTINCT BY`为每个不同的名称长度返回一行: ```sql SELECT DISTINCT BY ($LENGTH(Name)) Name,$LENGTH(Name) AS lname FROM Sample.Person ORDER BY lname ``` 下面的查询使用`DISTINCT BY`为`FavoriteColors` `%List`值的每个不同的第一个元素返回一行。它列出`FavoriteColors`为空的不同行: ```sql SELECT DISTINCT BY ($LIST(FavoriteColors,1)) Name,FavoriteColors,$LIST(FavoriteColors,1) AS FirstColor FROM Sample.Person ``` 以下查询按排序规则升序返回从`Sample.Person`检索到的前`20`个不同的`Home_State`值。`“top”`行反映`Sample.Person`中所有行的`ORDER BY`子句排序。 ```sql SELECT DISTINCT TOP 20 Home_State FROM Sample.Person ORDER BY Home_State ``` 以下查询在主查询和`WHERE`子句子查询中都使用`DISTINCT`。它返回`Sample.Person`中的前`20`个不同的`Home_State`值,这些值也在`Sample.Employee`中。如果未提供子查询`DISTINCT`,它将检索`Sample.Person`中与`Sample.Employee`中随机选择的`Home_State`值匹配的`DISTINCT Home_State`值: ```sql SELECT DISTINCT TOP 20 Home_State FROM Sample.Person WHERE Home_State IN(SELECT DISTINCT TOP 20 Home_State FROM Sample.Employee) ORDER BY Home_State ``` 以下查询返回前`20`个不同的`FavoriteColore`值。这反映了`Sample.Person`中所有行的`ORDER BY`子句排序。众所周知,`FavoriteColors`字段具有`NULL`,因此`FavoriteColors`为`NULL`的不同行出现在排序规则序列的顶部。 ```sql SELECT DISTINCT BY (FavoriteColors) TOP 20 FavoriteColors,Name FROM Sample.Person ORDER BY FavoriteColors ``` 还要注意,在前面的示例中,因为`FavoriteColors`是一个列表字段,所以归类序列包括元素长度字节。因此,以三个字母元素(红色)开头的不同列表值列在以四个字母元素(蓝色)开头的列表值之前。
文章
yaoguai wan · 九月 30, 2022

IRIS架构的浅显理解以及windows10、docker安装IRIS Health详解流程及部分问题浅析

前言 本人因技术需要,了解到了IRIS Health产品,在听了产品介绍会之后,感觉该产品是否有趣,并且比较符合自己目前的需求,因此大概了解了下IRIS的架构。以下是本人的浅显理解,如有错误之处欢迎讨论。 本人从产品介绍和社区的文档中,了解到IRIS的大概功能分类 InterSystems IRIS是一款数据平台,适用于软甲开发人员 基于FHIR 整合医疗全流程数据,通过机器学习和人工智能分析 业务优化 数据管理 Sharding数据分片技术 分布式架构 IRIS互操作性 数据分析能力 IRIS对FHIR的支持 机器学习与自动化 其中结合到自己想要研究的领域,想要探索是否可以利用该产品并结合其他工具开发一套通用的专病数据库构建及应用方法。 我目前对该产品的初步定位是对自行提供的数据集合的存储和处理,其中提供包括编码规范、高效存储架构、编程接口、算法在内的辅助工具。针对数据的处理和应用,该产品做的很完善,但是对于数据的获取,例如是否支持extract-transformation-load功能,或者能否利用自定义编程接口实现流批一体化数据抽取。根据上述两个问题,第一步需要安装该产品,所以本人根据社区官网上相关的安装教程进行了安装实验,在此期间发现了社区的教程有些简单并且有些关键点很容易被忽视本人在安装过程中就遇到过一个和系联的工程师讨论了一段时间之后才发现的,因此想与大家分享下安装过程中遇到的一些问题,避免一些问题后,安装过程还是很简单的,傻瓜式操作,后面可以分享下我的安装命令。 问题分享 首先注意产品的平台基本要求,这是平台操作手册上讲的,一定要严格遵守否则会报各种奇怪的错误,IRIS对不同版本的操作系统间的兼容性还有比较严格的,要注意,操作系统和后面容器的配合,我遇到的一个问题就是操作系统的版本和容器的版本不一致导致安装失败。 1、使用docker安装IRIS Health 安装平台介绍:Ubuntu18.04及docker 20.10 要求满足的容器版本20.04 安装过程中遇到的问题:注意linux的发行版本,必须严格遵守,本人在安装过程中最开始使用的是Ubuntu20,使用docker pull containers.intersystems.com/intersystems/iris-community:2022.1.0.209.0 命令可以顺利拉取镜像,但是在启动容器的过程中,一直失败,参考社区中有关启动失败时,设置cpu等限制sudo docker run --name my-iris --cpuset-cpus 0-7 -d store/intersystems/iris-community:2021.2.0.649.0,同样失败,最后发现是ubuntu的版本问题导致的,我最开始只关注了docker的版本,后来使用ubuntu18.04,即可顺利拉取镜像并启动容器。 附:1、查看ubuntu版本命令 cat /proc/version 2、查看docker版本命令 dicker version 2、windows安装IRIS Health 安装平台介绍:windows10(64位) 硬件介绍:16核cpu,32G运存,500G存储。注:因为本人采用的是虚拟机,所以各项配置初始时调整的很高,安装后发现社区版对各种性能有限制,例如cpu最多用8个,内存限制等,所以硬件环境无需太高,但是也不能太低,像windows10这种,首先要保证系统可以顺利安装, 在配置方面,Win10的配置要求其实并不高,具体配置如下: CPU:1GHz或更快的处理器   RAM:1GB(32位)或2GB(64位)   HDD:16GB(32位操作系统)或20GB(64位操作系统)   显卡:DirectX 9或更高版本(包含WDDM 1.0驱动程序)   显示器:1024x600分辨率   简而言之,只要满足或者高于以上要求即可安装。 但是考虑到iris还要占用很大一块运存和存储,所以windows10最少得4G运存和200G存储,这样系统运行不会太卡,iris运行速度也算可以(这个速度我只测试没有运行处理任务的速度)。 安装过程中遇到的问题:1、注意windows10的位数不要安装32位的操作系统,同时IRIS不支持windows7,一开始没注意,一直安装失败。 windows确定操作系统版本以及位数的命令不在赘述,google一下,很简单。 下一步计划 平台建好以后开始考虑数据的获取,下一步首先继续熟悉iris产品架构,然后尝试能否实现ETL与IRIS的对接。 好文,期待下一步分享! linux的安装可以参考一下马老师写的这篇文章https://cn.community.intersystems.com/node/516631,很nice
文章
Hao Ma · 十一月 14, 2022

ZPM介绍(1)

# ZPM介绍 有Developer听闻了InterSystems的包管理器ZPM, 希望我能介绍一下。正好刚刚看到一个开发者论坛的新闻:[ Open Exchange ZPM is now InterSystems Package Manager ](https://community.intersystems.com/post/zpm-now-intersystems-package-manager-ipm), 觉得更有必要了。 ## zpm是什么 简单说:zpm, 全称InterSystems ObjectScript Package Manager, 是一个包管理器, 开发者是Nikolay Soloviev和Dmitry Maslennikov。它先是在开发者社区里得到认可,以至于InterSystems开发者社区最近的一些比赛,要求参赛作品用zpm打包。然后就有了上面的链接的内容:InterSystems决定把它做为自己官方的打包工具, 将它改名字叫IPM(InterSystems Package Manager),同时保持它的开源状况不变。 这里我还是用zpm称呼它。两个原因。1. 操作的命令还是zpm, 所谓ipm,只是官方给的名字。2. ipm新的注册中心还不太了解,而且**到目前为止,IPM的注册中心还只对自己的雇员和付费用户开放**。本文的读者应该都还没来得及付费,所以暂时先放放,还是叫它zpm。 考虑到没有程序员背景的读者而对包管理器不熟悉,啰嗦两句。 ### 什么是包管理器 大多数开发工具都有自己的包管理器,Java开发使用MAVEN, Python用PIP, NodeJS使用npm, 它们细节有区别,功能大致是一致的,就是安装管理程序包,最基本的工作是: 1. 找到自己要的程序包。 简单的命令能让你在网上找到它,用名字找,或者其他方法搜索。 2. 安装包。 - 这里面最重要的是发现包之间的依赖。比如你要装一个软件包叫“包饺子”,作者在开发的时候使用了另一个软件叫“和面”,这个“和面”可能是作者自己写的,或者是网上其他人写的,注册在包管理器的。这时候作者在”包饺子“里声明,我依赖”和面“。 那么,当您去下载安装"包饺子“的时候,包管理器会自动的把”和面“也给你下载并安装上。 3. 管理包: 比如升级,删除等等。 ### ZPM的特点 没有zpm的时候,人们是怎么传递ObjectScript代码的? 大概是这样:发布者把代码从studio导出成一个XML;接受者拿到这个xml, 然后使用Studio或者iris管理门户把它导入到一个命名空间。 这里面有两个问题:1. 麻烦, 2. 没有打包各种类型文件的能力。通常一个项目,除非是只使用ObjectScript, 多数都用到各种文件:前端的css, html, javascript, 图片; 各种配置文件xml或者yaml, 其他语言的包 (iris支持嵌入式python,以及集成多种编程语言工作)等等。 ZPM是怎么工作的? 1. 作者把自己的数据放在一个公网地址上,比如github; 使用zpm生成一个配置文件(module.xml); 把module.xml发布到zpm的registry, 让别人能找到你的软件包。 2. 使用者在Registry搜寻并下载这个程序包,zpm命令自动把它导入到IRIS的一个命名空间。 为什么能自动导入到iris ? 这里有个ZPM和其他包管理器的很大的区别: **ZPM命令是在IRIS的terminal里执行的, 而不是操作系统上。** 还有一个特点:**zpm的设计假设大家开发代码使用的IDE是VSCode而不是Studio**,这非常关键。Studio上的开发是在服务端的开发,没有一个类应该存在哪个文件目录的概念。VSCode相反,你创建一个类:`Demo.Web.Test`, 那么默认的文件保存是在一个这样的目录下: `./src/Dome/Web/Test.cls` 不遵循这样的目录结构,您要额外做很多手工的调整才能使用zpm保存加载程序包。 这里还有很多问题, 我们后面详细说。 ## ZPM的下载安装 您可以从github上的InterSystems-Community用户的[zpm Repo](https://github.com/intersystems-community/zpm/wiki/01.-Getting-started)下载zpm-xxx.xml文件, 其中xxx是版本号, 当前(2022年10月)最新的版本是0.4.0。 把下载的xml文件导入到IRIS的**任何**命名空间。这样安装就成功了。 要下载安装软件的时候,您要先进入安装目标的命名空间(比如下面的USER),然后输入zpm, 也就进入了ZPM的操作界面: *ZPM Shell* ```sh USER>zpm ============================================================================= || Welcome to the Package Manager Shell (ZPM). || || Enter q/quit to exit the shell. Enter ?/help to view available commands || ============================================================================= zpm:USER> ``` 输入help, 您可以看到帮助文件。(帮助文件很长, 我只贴一小段。) ```sh zpm:USER>help -v Available commands: NOTE: [] around a parameter indicates it is optional arrange [flags] [] ■ Description: Rearranges the resources in a module manifest to follow the standard format # 此处省略许多行 ........ For more detail, run: help or help -v zpm:USER> ``` 接着, 我相信您一定想看看安装命令是怎么执行的,比如这样: ```sh zpm:USER>help install install [flags] [] ...此处省略若干行... ■ Examples ∙ install HS.JSON 1.x Installs the most recent 1.x version of HS.JSON available in any configured repository in the current namespace. zpm:USER>quit USER> ``` 好吧,让我们来下载一个最受欢迎的开发者应用“webTerminal”,了解最基本的zpm操作。 ## 最基本的ZPM操作 ### 搜索package 输入`zpm:USER>search`或者, 您可以使用`zpm:USER>search -r`,“-r"显示数据包所在的repo的位置。我没有贴在这里是因为显示的内容太宽了。在我的帖子的代码框里显示看上去有点乱。 ```sh zpm:USER>zpm:USER>search registry https://pm.community.intersystems.com: alwo-goselector 1.0.1 analytics-okr 1.0.0 ...此处省略许多行... zpm-shields 1.0.1 zpmhub 0.3.1 zpmshow 1.0.3 zpm:USER> ``` 除了search, 还有个find命令。 它可以使用*,但无法忽略大小写。 ```sh zpm:USER>find *Webt* registry https://pm.community.intersystems.com: zpm:USER>find *webt* registry https://pm.community.intersystems.com: webterminal 4.9.6 zpm:USER> ``` ### 安装package ```sh zpm:USER>install webterminal ...(省略若干行)... WebTerminal package successfully mapped into all namespaces. [USER|webterminal] Compile SUCCESS [USER|webterminal] Activate START [USER|webterminal] Configure START [USER|webterminal] Configure SUCCESS [USER|webterminal] Activate SUCCESS zpm:USER> ``` **到这里, 我来再次总结一下ZPM的最基本的功能:原本您想要使用一个其他开发者使用的程序,您要把它从某处,比如一个帖子,InterSystems的OpenExchange网站,或者一个github的主页上下载程序(XML文件),然后导入到IRIS执行。有了ZPM, 你可以在IRIS的terminal里通过ZPM命令,直接获得这个程序并执行。前提是:作者的程序是用ZPM打包并放在ZPM注册中心。** 来先了解一下ZMP的注册中心 ## 注册中心(Registry) 默认安装下, zpm的注册中心是这样的 ```sh zpm:USER>repo -list registry Source: https://pm.community.intersystems.com Enabled? Yes Available? Yes Use for Snapshots? Yes Use for Prereleases? Yes Is Read-Only? No Deployment Enabled? No zpm:USER> ``` 这也是当前zpm唯一的一个注册中心(配置私服会在后面介绍), 组册的软件包是最近一些年社区开发者写的各种工具和示例代码。说一下它的当前状况: - 当前能下载到278个软件包。 - 所有的包都放在了github。 - 包都很新。 这么说吧,release能到2.0的都是凤毛麟角 这里有个让人意外的事。我安装完webterminal并开始使用的时候,浏览器跳出这个提示。 “Welcome to WebTerminal! ...New update is available. ”。这说明这个registry并没有给出最新的webterminal版本。 我有点好奇,去看了一下webterminal的github page, 发现WebTerminal当前的版本是4.9.5。而zpm registry上下载的版本是4.9.2, 发行于2019年。 webterminal是一个前台工具,作者理所当然的的在代码里做了版本更新的检查并提醒了用户。而对于纯后台的ObjectScript语言的代码, 我估计大多数程序员都做不到这一点。 那么如果要保证其他人看到的是新版的软件, 就要求软件的作者,除了更新自己Github上的代码,还要把新版本及时更新在zpm的注册中心上。 文章太长了, 先贴一部分,后面会介绍 - 软件的发布 - 定义软件包的依赖 - 配置私服 *to be continued* 期待
文章
Qiao Peng · 一月 31

用Java开发互操作产品 - PEX

InterSystems IRIS、Health Connect和上一代的Ensemble提供了优秀的互操作架构,但即便有低代码开发能力,很多开发者还是希望能用自己的技术栈语言在InterSystems的产品上开发互操作产品。 考虑到互操作产品本身的开放性要求和各个技术栈背后庞大的生态价值,InterSystems IRIS和Health Connect提供了Production EXtension (PEX)架构,让开发者使用自己的技术栈语言来开发互操作解决方案。目前PEX支持Java、.net、Python。 这里我们介绍使用Java利用PEX进行互操作产品的开发。 一 InterSystems IRIS上使用Java开发的基础 在进入PEX主题前,需要简单介绍一下Java在InterSystems IRIS上开发的各种技术选项,因为PEX也是以这些技术选项为基础的。 如果仅把InterSystems IRIS作为一个数据库看待,可以使用JDBC对它进行SQL访问,这是最传统的开发方式; Java是一个面向对象的开发语言,而InterSystems IRIS也是支持面向对象的数据平台。InterSystems IRIS的外部语言服务器技术,提供了一套Java原生SDK,让Java和InterSystems IRIS可以互相访问对方的对象,甚至Java可以通过它直接访问InterSystems IRIS后台的持久化多维数组。外部语言服务器提供了非常灵活、高效的开发方式; 如果是想把Java对象直接持久化到InterSystems IRIS,除了Hibernate,InterSystems IRIS提供一种更高性能的方法 - XEP(Express Event Persistence表达事件持久化); 最后,Java可以通过PEX开发InterSystems IRIS的互操作产品组件。PEX基于Java原生SDK。 作为多种Java访问方式的基础,InterSystems IRIS的Java外部语言服务器的架构如下: InterSystems IRIS和Java通过动态代理对象的方式操作对方对象,动态代理无需在对方代码变更时重新生成代理类。 InterSystems IRIS提供了intersystems-jdbc-<版本号>.jar,不仅用于JDBC连接、也用于Java外部语言服务器连接,提供Java原生SDK。 在Java端通过jdbc.IRIS管理InterSystems IRIS连接,使用jdbc.IRISObject反向操作IRIS对象。 InterSystems IRIS端提供并管理包括Java语言服务器在内的一系列外部语言服务器。注意,一般情况下,使用系统开箱的这些外部服务器就够了,不需要额外建立额外的Java语言服务器。而且,也无需事先启动,外部语言服务器会在调用时自动启动。 Java连接到InterSystems IRIS,并创建IRIS外部服务器的示例: String connStr = "jdbc:IRIS://127.0.0.1:1972/USER"; String user = "superuser"; String pwd = "SYS"; // 使用DriverManager 创建IRIS连接 // IRISConnection conn = (IRISConnection)java.sql.DriverManager.getConnection(connStr,user,pwd); // 或使用IRISDataSource 创建IRIS连接 IRISDataSource ds = new IRISDataSource(); ds.setURL(connStr); ds.setUser(user); ds.setPassword(pwd); IRISConnection conn = (IRISConnection) ds.getConnection(); // 创建IRIS外部服务器 IRIS irisjv = IRIS.createIRIS(conn); // 调用IRIS类%PopulateUtils的类方法Name,产生随机姓名 String tempTrader = irisjv.classMethodString("%PopulateUtils", "Name"); 二 PEX 开发 PEX针对于互操作产品的开发,通过PEX使用外部语言开发互操作产品所需的组件,甚至可以通过PEX开发互操作产品的所有组件。 2.1 InterSystems IRIS互操作产品架构 典型的InterSystems IRIS互操作产品架构如下,它的典型工作机制是: 业务服务通过入站适配器监听外部系统,例如TCP和文件系统,将数据组织成消息发送给业务流程 业务流程根据流程需要在特定流程节点将消息发送给业务操作 业务操作通过出站适配器访问外部应用,发送数据并获取响应,并将响应数据组织成消息反馈给业务流程 在互操作产品内部流转的都是消息,消息默认自动持久化并可以通过SQL等方式结构化查询 互操作产品涉及3类组件: 业务组件:包括业务服务、业务流程、业务操作 适配器:包括入站适配器和出站适配器 消息 这3类组件都可以通过PEX进行开发。 2.2 PEX 类和类包 InterSystems IRIS 提供了一个intersystems-utils-<版本号>.jar包,提供com.intersystems.enslib.pex类包,用于基于Java开发互操作产品组件。 在Java代码里还要调用IRIS Object Script开发的组件和代码,因此,还需要intersystems-jdbc-<版本号>.jar。 2.3 示例业务场景介绍 下面我们根据一个示例业务场景介绍如何使用Java PEX进行互操作产品的开发。 业务场景是一个银行间汇款的流程: 业务服务(Finance.FromFileTransaction)监听特定文件夹,并从新汇款申请文件中解析出汇款请求,发送给业务流程(Finance.ProcessPaymentRequest); 业务流程先将请求发送给汇出行的业务操作(Finance.ToPayFromBank),获取汇出行的审核结果; 如果汇出行审核批准,则业务流程继续将请求发送给接收行的业务操作(Finance.ToPayToBank) 这里汇出行的业务操作和接收行的业务操作都会将数据写入特定文件夹的文件中。 这个场景中,我们完全使用Java开发全部的组件,包括:请求、响应消息;入站、出站文件适配器;业务操作;业务流程和业务服务。 下面我们就开始吧。 2.4 消息开发 Java消息类是com.intersystems.enslib.pex.Message的子类,消息中的属性,需要定义为public。它会在互操作业务组件间传递时自动持久化。 在IRIS端,Java消息自动映射到EnsLib.PEX.Message,它是一个持久化的IRIS类,包含以下属性: %classname属性:Java消息类名 %json属性:Java消息的JSON类型(IRIS动态对象) 在InterSystems IRIS端,例如消息可视化追踪页面和消息表,你看到的Java消息都是EnsLib.PEX.Message类型。 PEX消息用到的共同账号信息类的示例代码: package Finance; // 封装银行账号信息的类. public class PaymentProfile extends com.intersystems.enslib.pex.Message { public int AccountNumber; public int RoutingNumber; public String UserName; } 请求消息的示例代码: package Finance; import Finance.PaymentProfile; // PEX开发的转账请求消息. public class TransactionRequest extends com.intersystems.enslib.pex.Message { public double TransactionAmount; // 转账金额 public PaymentProfile PayFrom; // 付方银行账号信息 public PaymentProfile PayTo; // 收方银行账号信息 public String FromCurrency; // 付方货币 public String ToCurrency; // 收方货币 // 实例化银行账号属性. public TransactionRequest(){ PayFrom = new PaymentProfile(); PayTo = new PaymentProfile(); } } 响应消息的示例代码: package Finance; // PEX开发的转账响应消息. public class TransactionResponse extends com.intersystems.enslib.pex.Message { public boolean approved; } 2.5 业务组件和适配器的运行时变量 在互操作产品的管理配置页面,可以设置各个业务组件和适配器的配置项,例如文件适配器的文件路径,方便实施和管理。 对于Java开发的业务组件和适配器,同样我们需要这样的配置项。这些配置项,我们称之为运行时变量,这样进行定义: 运行时变量是Java业务组件类和适配器类的public类型的属性 对这些属性使用Java annotation进行元数据描述,例如: @FieldMetadata(IsRequired=true,Description="文件路径") public String FilePath; 后面的Java业务组件和适配器会用到运行时变量的声明方法。 2.6 入站适配器开发 如果要开发Java入站适配器,需要定义一个com.intersystems.enslib.pex.InboundAdapter的Java子类。并在类中实现以下方法: 方法 参数 说明 OnTask 无 适配器根据调用间隔设置,周期性地调用该方法对数据源进行查询;将数据调用BusinessHost.ProcessInput()将数据发送给业务服务;BusinessHost是业务服务的固定的变量名 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 例如入站文件适配器示例代码如下: package Finance; import java.io.File; // 导入File类. import java.io.FileNotFoundException; // 导入此类以处理错误. import com.intersystems.enslib.pex.FieldMetadata; // 导入PEX运行时变量类 // 这是一个PEX的入站适配器,检测定义在InboundFilePath的文件夹路径。如果文件夹下有文件,遍历这些文件,使用ProcessInput()将文件路径发送给业务服务 public class PEXInboundAdapter extends com.intersystems.enslib.pex.InboundAdapter { @FieldMetadata(IsRequired=true,Description="输入文件路径") public String InboundFilePath; // 文件夹路径配置项. public void OnTearDown() {} public void OnInit() {} public void OnTask() throws Exception { // 检查文件夹路径,并遍历文件. File folder = new File(InboundFilePath); File[] list = folder.listFiles(); if (list.length > 0) { for (File file : list) { // 忽略.keep后缀文件. // 其它文件路径发送给业务组件BusinessHost. if (file.getName().equals(".keep")) {continue;} BusinessHost.ProcessInput(file.getAbsolutePath()); } } } } 2.7 Java开发组件的注册与更新 开发好的Java组件,需要在InterSystems IRIS端进行注册,让互操作产品可以直接使用这些组件。 注册的方法在Production扩展页面,点击注册新组件: 在远程类名称中填写Java组件类名 在代理名称中填写IRIS生成的代理类名称,可以和Java类名不同。这个类名是在互操作产品中看到和使用的名字。 选择外部语言服务器为%Java Server 将封装有Java组件类的jar文件路径和其它依赖jar文件路径添加到网关额外CLASSPATHS下 注册后就可以在列表中看到注册的组件和组件类型,并在互操作产品中可以直接使用了。 如果后来修改了Java代码,则需要选中已经组册的组件,对其进行更新: * 记得下面这些Java开发的组件也需要注册后使用。 2.8 出站适配器开发 同样,可以通过继承com.intersystems.enslib.pex.OutboundAdapter来开发Java出站适配器类。 它需要实现以下方法: 方法 参数 说明 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 其它方法 根据需要 执行具体的适配器操作 例如出站文件适配器示例代码如下,它提供写文件的方法: package Finance; import java.io.File; // 导入File类. import java.io.FileNotFoundException; // 导入此类以处理错误. import java.lang.StringBuffer; // 导入string buffer类. import java.io.FileWriter; // 写文件的包. import java.util.Date; // 处理日期的包. import com.intersystems.enslib.pex.FieldMetadata; // 导入PEX运行时变量类. import java.text.SimpleDateFormat; // 格式化日期字符串的包. // 这是一个PEX的出站适配器,通常被业务操作使用,连接到外部系统. 这里适配器提供WriteToFile()方法,供业务操作调用. public class PEXOutboundAdapter extends com.intersystems.enslib.pex.OutboundAdapter { @FieldMetadata(IsRequired=true,Description="输出文件路径") public String FilePath; // 输出文件路径. public void OnTearDown() {} public void OnInit() {} //写文件的方法,输入参数input是向文件中写入的内容 public Object WriteToFile(java.lang.Object input) throws Exception { // 转换为字符串 String OutputMessage = (String)input; // 允许用户输入的路径有或没有尾部路径分隔符. if (FilePath.substring(FilePath.length() - 1) != File.separator) { FilePath = FilePath + File.separator; } // 用当前日期时间产生文件名. Date today = new Date(); SimpleDateFormat Formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); String DateString = Formatter.format(today); // 产生完整文件路径. String OutputPath = FilePath + DateString; // 创建文件. File OutputFile = new File (OutputPath); // 实例化FileWriter对象. FileWriter writer = new FileWriter(OutputFile); // 将内容写入文件 writer.write(OutputMessage); writer.close(); return null; } } 2.9 业务操作开发 Java业务操作类,是com.intersystems.enslib.BusinessOperation的子类。需要实现以下功能: 方法 参数 说明 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 OnMessage 无 个方法会在业务操作收到其它业务组件传递过来的消息时被调用 在该方法内部,可以调用适配器的方法 - Adapter.invoke("适配器方法名", 方法参数) Adapter是适配器对象,已经实例化,无需执行自己的实例化代码。Adapter是固定变量名 getAdapterType 无 返回使用的适配器类名 如果是PEX开发的适配器,返回的是它注册到InterSystems IRIS的代理类名 如果使用默认适配器,返回 Ens.InboundAdapter 如果不使用适配器,返回空字符串 例如我们的示例中,有以下2个业务操作: 1. 与汇出银行接口的业务操作类: package Finance; import Finance.TransactionRequest; import Finance.PaymentProfile; import Finance.TransactionResponse; import java.lang.StringBuffer; // PEX业务操作类,和支付银行接口. // 它从业务流程接收请求消息Finance.ProcessPaymentRequest, 将消息内容写入文件并返回内容为'approval' 的响应消息(Finance.TransactionResponse). public class ToPayFromBank extends com.intersystems.enslib.pex.BusinessOperation { public void OnTearDown() {} public void OnInit(){} // 确定适配器 public String getAdapterType() { return "Finance.PEXOutboundAdapter"; } // 响应请求消息 public java.lang.Object OnMessage(java.lang.Object object) throws java.lang.Exception { // 将输入对象转换为对应的类实例. TransactionRequest request = (TransactionRequest)object; // 初始化StringBuffer对象,构造输出文件内容. StringBuffer outputMessage = new StringBuffer(); // 将数据写入StringBuffer. outputMessage.append("Debit request:" + System.lineSeparator()); outputMessage.append("Route:" + request.PayFrom.RoutingNumber + System.lineSeparator()); outputMessage.append("Account: " + request.PayFrom.AccountNumber + System.lineSeparator()); outputMessage.append("Amount: " + request.TransactionAmount); // 调用出站适配器的方法,将数据写入文件. Adapter.invoke("WriteToFile", outputMessage.toString()); // 实例化响应消息,并对响应消息赋值后返回给业务流程. TransactionResponse response = new TransactionResponse(); response.approved = true; return response; } } 2. 与接收银行接口的业务操作类: package Finance; import Finance.TransactionRequest; import Finance.PaymentProfile; import java.lang.StringBuffer; // PEX业务操作类,和收款银行接口. // 从业务流程接收请求消息,并将消息内容写入文件. public class ToPayToBank extends com.intersystems.enslib.pex.BusinessOperation { public void OnTearDown() {} public void OnInit(){} // 确定适配器 public String getAdapterType() { return "Finance.PEXOutboundAdapter"; } // 响应请求消息 public java.lang.Object OnMessage(java.lang.Object object) throws java.lang.Exception { // 将输入对象转换为对应的消息类实例. TransactionRequest request = (TransactionRequest)object; // 实例化StringBuffer对象以构造文件内容. StringBuffer outputMessage = new StringBuffer(); // 将数据写入StringBuffer. outputMessage.append("credit request:" + System.lineSeparator()); outputMessage.append("SourceRouting:" + request.PayFrom.RoutingNumber + System.lineSeparator()); outputMessage.append("SourceAccount: " + request.PayFrom.AccountNumber + System.lineSeparator()); outputMessage.append("ToAccount: " + request.PayTo.AccountNumber + System.lineSeparator()); outputMessage.append("Amount: " + request.TransactionAmount); // 调用出站适配器方法将数据写入文件 Adapter.invoke("WriteToFile", outputMessage.toString()); // 无需响应消息. return null; } } 2.10 业务流程开发 使用Java开发业务流程,则完全是代码开发模式,而非基于BPL的低代码模式。 Java业务流程类是com.intersystems.enslib.pex.BusinessProcess 的子类,需要实现以下方法: 方法 参数 说明 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 OnRequest request 请求消息 处理发送给业务流程的请求消息,这个请求消息启动业务流程 OnResponse request 请求消息, response 响应消息, callRequest - 本次异步调用发起的请求消息, callResponse - 本次异步调用收到的响应消息, completionKey - 异步调用发起时传入的completionKey 处理异步调用返回的响应消息-callResponse OnComplete request 请求消息, response 响应消息 整个业务流程完成时,被调用的方法 另外,类里还可以声明需要在业务流程期间保存的持久化数据,和在BPL中要到的上下文(context)数据一样,例如记录汇出行的审核结果。声明时,需要加@Persistent这个Java annotation: @Persistent // 定义名为runningTotal的持久化的属性 public integer runningTotal = 0; 本用例的业务流程示例代码如下: package Finance; import Finance.TransactionRequest; //导入请求消息类 import Finance.TransactionResponse; //导入响应消息类 // 这是一个PEX业务流程类,它从业务服务(Finance.FromFileService)接收请求消息. // 先将汇款请求消息发送给汇出行业务操作(Finance.ToPayFromBank) 并等待审核结果. 如果批准, 将请求消息发送给接收行业务操作(Finance.ToPayToBank). public class ProcessPaymentRequest extends com.intersystems.enslib.pex.BusinessProcess { public Object OnComplete(Object object,Object object2) {return null;} public void OnTearDown(){} public void OnInit(){} // 无需返回响应消息给调用者 public java.lang.Object OnResponse(java.lang.Object request, java.lang.Object response, java.lang.Object callRequest, java.lang.Object callResponse, java.lang.String completionKey) throws java.lang.Exception { return null; } // 处理请求消息 public java.lang.Object OnRequest(java.lang.Object object) throws java.lang.Exception { // 将输入对象转换为对应的消息类实例. TransactionRequest request = (Finance.TransactionRequest) object; // 将支付请求消息以同步请求的方式发送给汇出行. Object response = SendRequestSync("Finance.ToPayFromBank", request); // 将收到的响应消息对象转换为对应的消息类实例. TransactionResponse bankResponse = (TransactionResponse)response; // 记录汇出行审核结果,用于调试 LOGINFO("汇出行审核结果是:"+bankResponse.approved); // 检查汇出行审核结果,如果是批准,则将汇款请求发送给接收行. if (bankResponse.approved) { // 使用异步调用方式通知接收行,无需等待返回. SendRequestAsync("Finance.ToPayToBank", request); } return null; } } 2.11 业务服务开发 Java业务服务类是com.intersystems.enslib.pex.BusinessService 的子类。它有一个父类定义的属性 – TargetComponentNames, 用于设置发送消息目标的下游业务组件名列表。可以同时发送消息给多个下游业务组件,组件名之间通过逗号分隔。 业务服务可以使用适配器,这时它会根据适配器的设置定期轮询外部系统,例如SQL适配器轮询外部SQL表、文件适配器轮询外部文件夹。但业务服务也可以不使用适配器,而直接被外部系统调用,例如发布为SOAP、RESTful服务的业务服务就不使用适配器。 适配器 执行过程 需要实现的方法 使用专业适配器 适配器的OnTask()方法定期执行获得数据,将数据发送给业务服务的ProcessInput() OnProcessInput() 使用默认适配器 Ens.InboundAdapter 适配器的OnTask()方法定期执行,并调用业务服务的ProcessInput(); 业务服务的OnProcessInput()执行数据获取 OnProcessInput() 不使用适配器 不定期执行,用户代码在需要时通过Director.CreateBusinessService() 创建业务服务,并执行其方法 OnProcessInput() 或其它需要的方法 本示例中的业务服务使用前面Java开发的文件入站适配器,其代码如下: package Finance; import java.io.File; // 导入File类. import java.io.FileNotFoundException; // 导入此类以处理错误. import java.util.Scanner; // 导入Scanner类读取文本文件. import Finance.TransactionRequest; // 导入请求消息类. // 这是一个PEX的业务服务类,从文件读取汇款请求. 将文件内容转换为Finance.TransactionRequest消息对象,并检查运行时变量TargetComponentNames,确定需要把请求消息发送给哪些业务组件. public class FromFileTransaction extends com.intersystems.enslib.pex.BusinessService { public String TargetComponentNames; // 用逗号分隔的发送目标组件列表,运行时从配置项获取. public void OnTearDown() {} public void OnInit() {} // 确定适配器 public String getAdapterType() { return "Finance.PEXInboundAdapter"; } // OnProcessInput根据业务服务的'call interval' 设置会被定期调用. public java.lang.Object OnProcessInput(java.lang.Object messageInput) throws java.lang.Exception { String path = (String)messageInput; File file = new File(path); // 创建scanner对象读取文件. Scanner reader = new Scanner(file); // 实例化请求消息TransactionRequest对象. TransactionRequest request = new TransactionRequest(); // 设置消息属性. request.TransactionAmount = Float.parseFloat(reader.nextLine().split(":")[1]); String tempString = reader.nextLine(); // 解析嵌套的PaymentProfile对象,并赋值给请求消息的PayFrom属性. String[] tempStringArray = tempString.split(":"); String[] PaymentProfile = tempStringArray[1].split("\\|"); request.PayFrom.AccountNumber = Integer.parseInt(PaymentProfile[0]); request.PayFrom.RoutingNumber = Integer.parseInt(PaymentProfile[1]); request.PayFrom.UserName = PaymentProfile[2]; // 解析PaymentProfile对象,并赋值给PayTo属性. tempString = reader.nextLine(); tempStringArray = tempString.split(":"); PaymentProfile = tempStringArray[1].split("\\|"); request.PayTo.AccountNumber = Integer.parseInt(PaymentProfile[0]); request.PayTo.RoutingNumber = Integer.parseInt(PaymentProfile[1]); // 设置剩余属性. request.PayTo.UserName = PaymentProfile[2]; request.FromCurrency = reader.nextLine().split(":")[1]; request.ToCurrency = reader.nextLine().split(":")[1]; reader.close(); // 处理后删除文件. file.delete(); // 获取Split target business component string and send to each component. String[] targetNames = TargetComponentNames.split(","); for (String name : targetNames){ SendRequestAsync(name, request); } return null; } } 2.12 组装互操作产品并进行测试 将这些Java开发的组件组册后,就可以像InterSystems IRIS的本地组件一样使用了。我们将业务操作、业务流程和业务服务依次加入到Production,并配置好相应的配置项: 对于每个业务操作,需要配置一项: 文件路径:输出的文件夹路径 对于业务服务,需要配置两项: InboundFilePath: 监听的汇款请求文件夹路径 TargetComponentNames: 目标业务流程,这里应该填Finance.ProcessPaymentRequest 下面是用于测试的汇款请求文件内容,把它copy到一个文本文件中保存: TransactionAmount:59.43 PayFrom:232422|23532532|TJohnson21 PayTo:24224242|423533453|ERichards55 FromCurrency:USD ToCurrency:USD 启动Production后,可以将测试数据文件copy到业务服务监听到文件夹下。如果成功,应该可以看到这样的消息追踪结果,可以看到,用Java开发的PEX组件运行结果和IRIS自带的组件没有区别: 2.13 需要实现的Java类方法总结 下图总结了用Java开发不同组件时,需要实现的方法,以及这些组件间的调用流程和关系: 完整的规范参见这里。 三 在Java组件中使用Object Script开发的组件 上面是使用Java开发全套互操作产品组件的示例。实际情况下,InterSystems IRIS已经提供了大量适配器、消息和业务组件,而且使用低代码工具 - 例如业务流程建模图形化工具 - 往往更方便,所以通常Java可以使用Object Script开发的大量组件,而无需全部由Java开发。 在低代码开发业务流程时,可以直接在BPL里调用Java PEX开发的业务流程和业务操作,和IRIS本地开发的组件无异。 另外两种典型的用例是Java组件使用Object Script的消息、Java组件使用Object Script的适配器。在Java端使用Object Script类和对象,需要有前面讲的Java外部语言服务器的基础,文档参见这里。 3.1 Java组件使用Object Script的消息 在Java PEX中使用Object Script定义的消息,直接使用IRIS类的反向代理类IRISObject即可。例如在Java PEX业务操作的OnMessage方法中,要获得传入的Object Script消息的属性值,只需要把传入消息对象转换为IRISObject对象,并调用它的getXXX方法获取属性即可,可以这样: @Override public Object OnMessage(Object object) throws Exception { // 处理类型Ens.StringRequest的ObjectScript消息,获取StringValue属性的值 // 使用ObjectScript消息,将Object转换为 IRISObject IRISObject requestObj = (IRISObject)object; 通过IRISObject的getString方法获取它名为StringValue属性的值 String value = requestObj.getString("StringValue"); ... } 那怎么在Java组件中创建新的Object Script消息实例呢?你需要已经建立了IRIS连接的类型为com.intersystems.jdbc.IRIS的实例。 PEX的Java业务组件类和适配器类都继承于Common类,从Common类继承一个公共属性irisHandle,类型为IRISObject。而irisHandle有一个公共属性iris,类型为com.intersystems.jdbc.IRIS。因此可以通过它来创建Object Script消息实例。例如我们要创建一个类型为Ens.StringContainer的响应消息,并给其StringValue属性赋值: @Override public Object OnMessage(Object object) throws Exception { // 处理类型Ens.StringRequest的ObjectScript消息,获取StringValue属性的值 // 使用ObjectScript消息,将Object转换为 IRISObject IRISObject requestObj = (IRISObject)object; String value = requestObj.getString("StringValue"); // 创建一个类型为Ens.StringContainer的ObjectScript响应消息 IRISObject response = (IRISObject)(this.irisHandle.iris.classMethodObject("Ens.StringContainer","%New")); response.set("StringValue",value+"test me"); return response; } 3.2 Java业务组件使用Object Script的适配器 如果Java业务服务使用Object Script的适配器,无需做任何处理,适配器会将数据发送给Java业务服务的OnProcessInput方法,而Java业务服务只需要将接收的数据转换为IRISObject类型进行处理即可。 如果是Java业务操作要使用Object Script适配器,则需要调用适配器的方法。调用适配器方法,可以使用Adapter属性的invoke方法调用适配器类的方法,语法是 Adapter.invoke("方法名", 方法参数),例如: // 使用Object Script的出站文件适配器(EnsLib.File.OutboundAdapter),并调用它的Exists方法检查是否存在文件a.txt Object obj = Adapter.invoke("Exists", "a.txt"); 虽然这里介绍的是使用Java PEX开发互操作产品组件,您也可以使用.net 和Python进行开发,而且任何语言开发的PEX组件都可以一起使用。通过PEX,可以充分利用已有的生态代码和组件快速开发互操作产品。
文章
YuHao Wan · 十一月 1, 2022

Caché实现SM3密码杂凑算法

### 0. 算法概述 SM3密码杂凑算法是中国国家密码管理局2010年公布的中国商用密码杂凑算法标准。该算法于2012年发布为密码行业标准(GM/T 0004-2012),2016年发布为国家密码杂凑算法标准(GB/T 32905-2016)。 SM3适用于商用密码应用中的数字签名和验证,是在[SHA-256]基础上改进实现的一种算法,其安全性和SHA-256相当。SM3和MD5的迭代过程类似,也采用Merkle-Damgard结构。消息分组长度为512位,摘要值长度为256位。 整个算法的执行过程可以概括成四个步骤:**消息填充、消息扩展、迭代压缩、输出结果**。 ### 1. 消息填充 SM3的消息扩展步骤是以512位的数据分组作为输入的。因此,我们需要在一开始就把数据长度填充至512位的倍数。具体步骤如下: 1、先填充一个“1”,后面加上k个“0”。其中k是满足(n+1+k) mod 512 = 448的最小正整数。 2、追加64位的数据长度。 ![消息填充](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-01.png) ### 2. 消息分组扩展 将填充后的消息m′按512比特进行分组:m′ = B(0)B(1)...B(n−1) 其中n=(l+k+65)/512。 SM3的迭代压缩步骤没有直接使用数据分组进行运算,而是使用这个步骤产生的132个消息字。(一个消息字的长度为32位/4个字节/8个16进制数字)概括来说,先将一个512位数据分组划分为16个消息字,并且作为生成的132个消息字的前16个。再用这16个消息字递推生成剩余的116个消息字。 ![消息分组](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-02.png) ### 3. 迭代压缩 SM3使用消息扩展得到的消息字进行迭代运算。 ![迭代运算](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-03.png) 初值IV被放在A、B、C、D、E、F、G、H八个32位变量中,整个算法中最核心、也最复杂的地方就在于**压缩函数**。压缩函数将这八个变量进行64轮相同的计算。 ![压缩函数](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-04.png) ### 4. 输出结果 将得到的A、B、C、D、E、F、G、H八个变量拼接输出,就是SM3算法的输出。 ![杂凑值输出](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-05.png) ### 5. 附录 ![符号](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-07.png) ![常数与函数](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-06.png) ### 6. Caché实现 ``` /// SM3密码杂凑算法是中国国家密码管理局2010年公布的中国商用密码杂凑算法标准 /// 该算法于2012年发布为密码行业标准(GM/T 0004-2012),2016年发布为国家密码杂凑算法标准(GB/T 32905-2016)。 /// 对长度为l(l < 2^64)比特的消息m,SM3杂凑算法经过填充和迭代压缩,生成杂凑值,杂凑值长度为256比特。 Class Utility.SM3 Extends %RegisteredObject { /// Creator: wyh /// CreatDate: 2022-11-01 /// Description:SM3加密 /// Input: msg:原文 /// Output: 16进制密文 /// Debug: w ##class(Utility.SM3).Hashsm3("{""appId"":""60C90F3B796B41878B8D9C393E2B6329"",""nonceStr"":""1234567890"",""timestamp"":""60C90F3B796B41878B8D9C393E2B6329"",""version"":""V2.0.0""}F2D8D966CD3D47788449C19D5EF2081B") ClassMethod Hashsm3(msg) { #; 初始值IV =7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e s V(0) = "01110011100000000001011001101111 01001001000101001011001010111001 00010111001001000100001011010111 11011010100010100000011000000000 10101001011011110011000010111100 00010110001100010011100010101010 11100011100011011110111001001101 10110000111110110000111001001110" #; 1. 消息填充 s m = ..s2m(msg) #; 2. 消息分组 #; 将填充后的消息m′按512比特进行分组:m′ = B(0)B(1)...B(n−1) #; 其中n=(l+k+65)/512。 s n = $l(m)/512 f i = 0 : 1 : n-1 d .s B(i) = $e(m, 512 * i + 1, 512 * (i + 1)) #; 3. 迭代压缩 #; 对m′按下列方式迭代: #; FOR i=0 TO n-1 #; V(i+1) = CF(V(i),B(i)) #; ENDFOR #; 其中CF是压缩函数,V(0)为256比特初始值IV,B(i)为填充后的消息分组,迭代压缩的结果为V(n)。 .s V(i+1) = ..CF(V(i) ,B(i)) #; 4. 杂凑输出 #; ABCDEFGH = V(n) #; 输出256比特的杂凑值y = ABCDEFGH。 s rtn = "" f i = 1 : 1 : 8 d .s bit = $p(V(n), " ", i) .s num = ..bs2n(bit) .s hex = $ZHEX(num) .i $l(hex) < 8 d ..f j = 1 : 1 : 8 - $l(hex) d ...s hex = "0" _ hex .s rtn = rtn _ hex s rtn = $zcvt(rtn, "L") return rtn } /// 1. 消息填充 /// 将填充后的消息m′按512比特进行分组:m′ = B(0)B(1)...B(n−1) /// 其中n=(l+k+65)/512 ClassMethod s2m(msg) { s len = $l(msg) s r = "" f i = 1 : 1 : len d .s num = $ascii($e(msg, i)) .s bs = ..n2bs(num) .s r = r _ bs s rtn = r s rtn = rtn _ "1" s k = 512 - (64 + ($l(r) + 1)) # 512 f i = 1 : 1 : k d .s rtn = rtn _ "0" s lenbs = ..n2bs($l(r)) s t = 64 - $l(lenbs) f i = 1 : 1 : t d .s rtn = rtn _ "0" s rtn = rtn _ lenbs return rtn } /// 3. 迭代压缩 /// 令A,B,C,D,E,F,G,H为字寄存器,SS1,SS2,TT1,TT2为中间变量,压缩函数V(i+1) = CF(V(i),B(i)), 0
文章
Hao Ma · 十一月 22, 2023

访问 IRIS 终端:Visual Studio Code 用户综合指南

介绍 由于InterSystems最近宣布从2023.2版本开始停止对InterSystems Studio的支持,转而独家开发Visual Studio Code(VSC)IDE的扩展,相信后者比Studio提供了更优越的体验,我们很多开发者都已切换或开始使用 VSC。很多人可能想知道如何打开终端进行操作,因为VSC没有像Studio那样的输出面板,也没有集成的功能来打开IRIS终端,除非下载InterSystems开发的插件。 概括 介绍 解决方案 对于至少具有 IRIS 2020.1 或 IRIS IRIS 2021.1.2 的用户– 使用 Web 终端 对于至少具有 IRIS 2023.2 的用户 – 使用 WebSocket 终端 对于使用基于 Docker 的 IRIS 的用户 对于在本地计算机上使用 2023.2 之前的 IRIS 版本的用户 对于使用 SSH 连接在基于远程服务器的 IRIS 上进行编码的用户 解决方案 在 VSC 中打开终端的方法有多种,具体取决于您使用的具体配置,我在这里总结了适合任何情况的最佳解决方案: 对于至少具有 IRIS 2020.1.1 或 IRIS 2021.1.2 的用户 – 使用 Web 终端 至少拥有 IRIS 2020.1.1 或 IRIS 2021.1.2 且被允许安装外部扩展的用户(某些人可能由于公司有关第三方应用程序的政策而不允许安装),可能会发现 VSC 的 Web 终端扩展很有用。谁不知道, Web 终端是一个基于 Web 的 InterSystems 产品终端,使用 ObjectScript(例如 IRIS、Caché、Ensemble、HealthShare、TrakCare)构建,允许在浏览器中使用更高级版本的终端( 这里是项目页面)。通过此 VSC 扩展,只需单击一下即可直接从 VSC 启动基于 Web 的终端。 要打开 Web 终端,请单击: InterSystems Tools > 选择一个名称空间 > 单击以下图标之一 ( , )在 VSC 终端面板或浏览器上打开 Web 终端(按 Alt 更改默认图标): 对于至少具有 IRIS 2023.2 的用户 – 使用 WebSocket 终端 至少拥有 IRIS 2023.2 的用户可以利用最新版本的 VSC 扩展中包含的新“ WebSocket 终端”功能,并且不需要其他解决方法。 要打开 WebSocket 终端,请单击: InterSystems Tools > 选择一个名称空间 > 单击 Web 终端旁边的图标。 对于使用基于 Docker 的 IRIS 的用户 在 Docker 中使用 IRIS 环境并使用 VSC 的人员可以直接在 Docker 环境中启动终端会话。 单击状态栏中的 Docker 语音,然后选择Open Terminal in Docker 。 我要感谢@Evgeny.Shvarov 有关这一点的图片和解释。 对于在本地计算机上使用 2023.2 之前的 IRIS 版本的用户 对于使用在本地计算机上运行的 IRIS 版本的用户,可以在 VSC 中设置专用的 IRIS 终端: 打开设置.json 文件。您可以通过多种方式找到它,例如单击“视图”> “命令面板”> 输入:“设置”>打开用户设置 (JSON) 在“ terminal.integrated.profiles.windows ”下添加以下代码: "terminal.integrated.profiles.windows" :{ "IRIS Terminal" : { "path" : [ "C:\\InterSystems\\IRISHealth\\bin\\irissession.exe" ], "args" : [ "IRISHEALTH" ], "icon" : "terminal-cmd" } } 注意:插入irissession.exe的正确路径。 C。要从 VSC 打开终端,请导航至:终端>新终端>启动配置文件... > IRIS 终端。 d.终端菜单中现在应该可以使用“IRIS Terminal”语音: 对于使用 SSH 连接在基于远程服务器的 IRIS 上进行编码的用户 对于使用基于可通过 SSH 连接(例如使用PuTTY )访问的远程服务器(例如公司服务器)的 IRIS 版本的人员来说,可以使用远程 - SSH VSC扩展将 VSC 直接连接到服务器。为此: 安装远程 - SSH:编辑配置文件 VSC 扩展; 单击“远程资源管理器”图标 在侧边栏中; 选择“打开 SSH 配置文件” 并打开配置文件,路径为: C:\Users\<用户名>\.ssh\config 在配置文件中插入以下代码: Host my-putty-connection HostName < IP address or server name > User < username > IdentityFile < private key path on your local machine > Port < port > IP 地址和端口对应于 PuTTY 中指定的主机名和端口,用户名是用于访问远程服务器的用户凭据,IdentityFile 是 PuTTY 私钥的文件路径。 注意:VSC 无法读取 PuTTY (.ppk) 生成的私钥的原始格式。要通过 PuTTY 在 VSC 和远程服务器之间建立连接,您必须复制原始私钥并将新版本转换为 .pem 格式。为了进行转换: 启动 PuTTYgen 应用程序 在“文件”菜单下,单击“加载私钥” 选择 .ppk 格式的私钥,然后选择“打开” 在“转换”菜单下,单击“导出 OpenSSH 密钥”(强制使用新文件格式)。 设置扩展名为 .pem 的新名称,然后单击“保存”按钮。 将此新 .pem 文件的路径链接到 VSC 中的IdentifyFile 参数 保存文件。几秒钟后,新连接应出现在“远程资源管理器”面板中; 单击“在新窗口中连接... ”以在新的 VSC 窗口中打开 SSH 连接: 选择远程计算机的操作系统(仅在第一次访问时) 在新窗口中,导航至:终端>新终端(或使用快捷键 Ctrl + ò 或 Ctrl + Shift + ò)。 您现在已连接到远程计算机,并且可以在 VSC 中使用其 IRIS 终端。 注意:此操作仅在您之前通过 PuTTY 启动远程连接时有效,并且在 PuTTY 关闭或未连接到远程服务器时不起作用。此操作不会启动 PuTTY,它仅允许 VSC 连接到 PuTTY 建立的隧道。 要通过 VSC 启动 PuTTY 连接,您可以使用批处理文件(在 Windows 上)。提供的connect_remote.bat文件使用 PuTTY 附带的 Plink 命令来启动会话: @echo off set SESSION="<your saved session name>" plink -load %SESSION% 要启动会话,只需在 VSC 终端中键入.\connect_remote.bat以打开远程连接并插入您的凭据。 注意:后一种方法使您可以访问支持所有 VSC 快捷方式的终端版本!欢迎回来 Ctrl+V,再见 Shift+Insert 🎉
文章
Qiao Peng · 十二月 2, 2022

通过智能数据编织应对数据挑战

1.数据的价值 数据的核心价值是帮助我们决策。 我们无时无刻不在决策,大到战略决策——为一家新医院选址,还有战术决策——鉴别产品的目标市场或抵押贷款审批,更频繁的是操作决策——决定患者的手术方案或患者药物的调整。 这些决策要求不同的决策速度,传统的数据中心已经能较好地帮助我们做战略决策、战术决策,甚至一些操作决策。但新的业务需求要求我们的决策速度越来越快,甚至借助机器学习自动为我们做出即时的决策,例如批准还是拒绝一笔信用卡交易或基于算法自动交易。 无论是人工决策还是基于机器学习的自动决策,决策的依据是数据。数据的速度和质量决定了决策的速度和质量。要支持决策,需要数据具有如下特征: (1)完整 :关联且具有完整上下文; (2)干净 :数据质量没有问题; (3)及时 :在决策点上没有延迟。 传统数据中心很难在及时性上满足要求。 2.数据挑战 数字化浪潮下,我们面临更大的数据挑战: 数据规模:数据量已经完全超出了人工处理能力。 数据源多样性:数据源不再仅是数据库,流式引擎的消息、物联网、对象存储......它们还带来了越来越多模型种类的数据。 更多的数据孤岛:更多的系统和应用被建设,进一步增加了数据孤岛现象。 跨部门的数据不一致:统计口径和统计时间的差异,造成财务部门统计的数据,总是和业务部门统计的数据对不上。 数据服务对象变化:现在业务分析师、运营数据消费者、数据工程师、数据科学家和普通人群都是服务对象。 部署需求的多样性:传统本地部署、云部署、混合部署...... 而由于技术、法律、经济性等原因,传统的数据集中保存无以为继...... 数据库、数据仓库、数据湖,这些传统的数据管理技术应对这些需求和挑战,越来越力不从心。数据库能保持数据的热度(良好的数据存取速度),但支持的模型类型和数据来源有限;数据仓库要统一数据质量与格式(Schema on Write),缺乏灵活性;数据湖可以“以原始形态保存一切数据” (Schema on Read),但各种数据进入这样一个湖,全都变成了无法直接分析利用的冷数据! 应对之道 – 数据编织和智能数据编织 数据编织是正在兴起的数据管理技术以应对数据挑战,Gartner将其定为2022年12大战略技术趋势之首。 那什么是数据编织? Gartner将数据编织定义为一种设计概念,可作为数据和连接流程的集成层(结构)。通过对现有、可发现和可推理的元数据资产进行持续分析,数据编织能够在所有环境(包括混合云和多云平台)中设计、部署和利用可重复使用的集成数据。 智能数据编织(Smart Data Fabric)则更进一步,在结构中直接嵌入各种分析能力,包括数据探索、商业智能、自然语言处理和机器学习,使企业可以更快、更容易地获得全新洞察,为智能预测和规范性服务及应用提供动力。 Gartner的这个名词解释还是有些抽象,如何理解它?为何数据编织/智能数据编织是解决上述挑战的利器? 如何利用现有的产品真实实现智能数据编织的落地? InterSystems提供的智能数据编织解决方案 今天,智能数据编织(Smart Data Fabric)正被用于许多行业的实际生产中,为各种企业级、关键任务创新提供动力,包括场景规划和决策支持、法规遵从、实时可见性和警报等,作为全球领先的数据技术提供商,为应对当前数据挑战,特别通过InterSystems IRIS新一代数据平台提供智能数据编织解决方案,整合了许多关键特性和能力,以满足客户实施智能数据编织进行数字化转型的需求,该方案在解决数据挑战的同时,允许现有的遗留应用和数据保持原位,最大限度地利用以前的技术投资,包括现有的数据湖和数据仓库,而不需要“撕裂和替换”任何现有技术。 InterSystems IRIS数据编织解决方案把智能数据编织分为3个阶段: 数据互联互通阶段:有能力实时、双向打通各种数据源,将数据源有机编织在一起。 数据集成阶段:对数据本身进行编织,为多模型数据提供高性能存取和转换、加入数据安全控制、建立数据谱系、抽象为干净和统一的语义层供数据用户使用。 智能利用阶段:对建立了统一语义的数据提供紧贴数据的智能利用能力,例如商业智能分析、自然语言分析、机器学习,并使这些智能增强数据编织本身。 InterSystems IRIS数据平台在单一技术栈内提供智能数据编织的这些能力:互联互通、数据集成、自助服务、智能分析和多云 。 传统数据利用的是多级瀑布模式:数据从数据库到数据湖,再到数据中心,数据大批量、高延时地在异构数据平台间移动和拷贝。这是影响数据时效性、一致性的主要原因。所以InterSystems智能数据编织第一就要解决这个问题,而解决之道就是互联互通和数据集成。 1.互联互通 因为数据源和数据模型的多样性,传统的ETL在能力和时效性上都已不能满足需求,需要更完整的互联互通能力。长期以来,InterSystems是互操作技术的领导者,它提供各种适配器实时接入各种数据源,例如流式处理引擎Kafka,并对遗留系统进行现代化,即便有很多遗留系统作为数据源,依然可以通过它将其数据模型多态化和数据服务现代化。 2.数据集成 数据集成(Integration)不追求将数据放在一起,而是要建立数据间的准确关联,建立具有连续上下文的全息数据,甚至丰富数据。InterSystems提供: (1)多模型 面对多元数据,Gartner表示,要想成功利用数据编织,企业必须确保数据编织能够动态地(通过元数据驱动设计)支持不同数据交付风格的组合,以支持特定的用例。 InterSystems的多模型数据建模和保存能力,让不同的数据以最适合的模型进行操作,它支持原生的对象、表、键值对和JSON文档。 (2)多语言 如何操作多模型的数据?每个数据用户都有熟悉或适合其用途的语言来使用数据,例如很多场景下,SQL是最简单的使用数据的语言。InterSystems让用户可以用SQL操作一切数据,哪怕它是以键值对建模和保存的。 (3)数据转换 不同的数据用户希望得到不同结构的数据。InterSystems提供图形化的高效数据转换工具,为用户构建干净的、单一可信的数据。 3.自助服务 如何发现、探索、推理数据编织平台中的数据?需要借助统一的语义和自助的服务能力。 (1)统一语义 为了数据完整性,无论是数据仓库还是数据湖,都将数据中心化存储。这造成了很多数据障碍:数据的时效性低、数据的质量参差不齐、数据需求严重依赖IT去清洗关联等等。 InterSystems的自适应分析是一个统一的、抽象的语义层,通过建立虚拟/逻辑数据分析立方体,用户可以使用SQL或BI工具访问这个语义层,而自适应分析自动使用SQL访问后台的多InterSystems IRIS数据平台实例获得数据和分析结果,不需要将数据集中保存到一起。 数据无需集中,因此无需ETL,没有数据抽取拷贝的时间成本开销,提供高时效性的数据;而抽象语义层将多数据源的数据建立逻辑关联,向用户提供干净、完整的语义上下文。 (2)行业语义级的数据编织 医疗健康行业面临相较其它行业更复杂的行业数据,在现实业务中要应对不同的语义表达。编织不同语义的数据源,将数据抽象为非标准语义,这会为后面的数据价值利用增加障碍。 FHIR建立统一行业语义的行业数据内容标准、利用标准行业术语和标识符、定义统一的传输标准、并逐步建立隐私和安全标准,让使用行业语义编织数据成为可能。 InterSystems支持所有FHIR的交互范式,提供FHIR服务器和FHIR资源仓库,并通过FHIR SQL构建器,建立基于SQL的FHIR数据访问能力,用最简单的数据操作支持BI/AI。通过FHIR来搭建具有统一行业语义和生态的数据编织平台。 (3)自助服务 长久以来,由于数据源和数据本身的复杂性,专业IT用户把持着数据的使用,商业用户极度依赖于专业用户才能获取、分析和利用数据。 借助统一语义层和对标准的支持,InterSystems让商业用户使用自己熟悉的工具和语言,例如SQL、BI工具和API来探索数据、操作数据和分析数据。 4.智能分析 为数据编织增加智能, InterSystems提供开放的智能分析能力。包括嵌入平台的机器学习、自然语言分析、商业智能特性,对第三方工具和生态的支持,以及对标准的支持——MDX、UIMA、PMML...... InterSystems提供全SQL操作方式的自动化机器学习,并允许使用第三方的自动化机器学习后台,如DataRobot,从而避免学习不同的API,用最简单的SQL就可以获得丰富的机器学习能力: 5.多云 数据编织平台要能支持所有主要的开发和部署环境,使开发人员和运营团队能够在他们选择的环境中工作。并与现有的基础设施和最佳技术无缝集成,支持最广泛的客户环境和应用要求。 公共云、私有云、本地、混合、裸机和虚拟机环境,InterSystems支持所有部署选项,且都只需要一个API,而不需要对你的应用程序进行修改。 6.单一技术栈而不建议不同产品和技术的堆砌与粘合 这一点至关重要。从整个数字化、信息化行业来看,信息部门尤其是最终用户的人员配置是远远不足的,从信息化部/科技部到我们的服务厂商,大部分从业人员工作负载基本都处在一个饱和甚至过饱和的状态,同时,IT从业人员由于薪酬激励的原因,大部分的中高端IT人才集中在少数行业或者少数头部厂商。针对这种情况,在数智底座的技术选择上,应该采取“少就是多”的策略,选择尽可能少的技术和产品种类,或者说一体化的技术架构,用一套技术体系来支持多种业务应用的实现,从而降低管理和学习成本。 另外一个原因,比如传统闭源厂商,很多软件产品都是收购或者是不同团队部门研发的,不同软件之间也需要集成或者胶水(参考文章:统一技术的高效数据架构能帮助客户节约大量成本)成本也是极其昂贵的。如果是选择开源技术栈,除了集成以外,还要考虑更多的比如技术路线的生命周期持久度,比如今天选择的技术会不会很快就过时了,技术支持如何延续?此外开源技术人员的成本普遍高于传统软件,人员流动也比较强,这也是企业选型必须考虑的问题。 总结 InterSystems的智能数据编织解决方案通过对数据源、数据的编织,避免多级瀑布式的大规模、高延迟的数据拷贝。构建抽象的统一语义层,并借助行业标准语义,建立基于标准的数据编织平台,为用户提供简单易用的数据探索和利用能力。利用全面的智能分析能力提升数据质量和数据价值,并降低数据利用的难度。多云的架构确保了对最广泛的客户环境和应用要求的支持,为数智底座的实施部署铺平了道路。
文章
Louis Lu · 一月 19, 2023

HL7 V2.5.1 的查询与结果返回

这篇文章主要介绍 HL7 V2.5.1 标准是如何定义查询类请求,以及查询类响应的。相关HL7 V2 的更多基础知识可以参考:HL7v2到底是什么?! 的一系列文章。 1 查询标准的发展 1.1 最早的查询模式 最初,HL7的查询参数通过QRD以及QFR 字段传入。因为这两个字段的设计是为了满足所有的查询需求,所以这两个字段的定义非常随意。 1.2 加强的查询模式 从HL7 V2.3开始,引入了加强版的查询模式,它包含了四种方式: • 嵌入式查询语言类请求查询:自由格式的select SQL语句 • 虚拟表类请求查询:基于特定的select 条件查询服务端的数据库表 • 存储过程类请求查询:执行服务端的存储过程返回数据 • 事件类请求查询:返回基于特定事件的查询结果 1.3 基于2.4 版本的查询 HL 7 v2.3.1之后的版本更清晰地将请求查询的方式与返回查询数据的方式分开,并且强调了“符合性声明”的存在。 HL 7继续支持存储过程、事件查询和虚拟表查询的语义,但推荐使用新的查询方式,即按参数查询(QBP),使语法更清晰。 QBP查询的目的是在一个精确的一致性声明的框架内统一存储过程、事件和虚拟表查询的语义。 同时该标准仍可以继续使用最初模式查询(QRD/QRF),但使用新的查询形式可以更清楚地解释其语义。 2 符合性声明Conformance Statement 符合性声明很像我们熟悉的“接口文档”,在其中定义了哪些数据是可用的,数据将如何被返回,以及哪些变量可以在查询中被赋值以及其约束范围。典型的符合性声明应由下面的内容组成: 介绍部分包含标题、触发事件、模式、特点和目的 查询语法 返回语法 输入规范和注释 返回控制 输出规范和注释 更多符合性声明文档的解释和例子可以参考HL7官方文档。 3 消息格式 正如前面说的,HL 7 v2.3.1之后的版本更清晰地将请求查询的方式与返回查询数据的方式分开,这里重点介绍这两个不同的方式。每种消息的示例会在文章最后给出。 3.1 返回查询结果数据 HL7 定义了三种返回查询结果数据的格式:分段、表格或显示格式。分段格式的响应是由一组HL7段组成。每个查询都会在符合性声明中定义它将返回的HL7片段每个字段的含义。表格式响应是以一组行的形式返回数据,每行一个RDT段。最后的显示查询是以DSP段承载返回数据。 3.1.1 分段响应格式 分段格式的返回是HL7提供数据的传统方式。服务器通过返回HL7段的方式对查询作出响应。例如,对检验数据查询的响应的核心可能由以下分段语法定义。 { PID OBR [{OBX}]。 } 其中,病人信息将在PID段中返回,实验室检验结果在OBR和OBX段中返回。在这种模式下,服务器返回的消息通常与现有的非请求类HL7消息非常接近。 在为分段模式的返回内容定义一致性声明时,数据所有者必须决定它将返回的确切段语法。它应该在必要时阐明每个字段的含义、数据的数量,以及数据是可选的还是必须的。 3.1.2 表格响应格式 表格模式的返回是一个相当传统的由行和列组成的表格。行和列的具体含义会在在该查询的符合性声明中被完整的定义。 当所返回的信息相对简单时,以表格的方式是合适的。但对于涉及复杂的结果嵌套的检验报告来说,它并不是很合适。同时典型的HL7段或段组所携带的数据也可以被建模为一个表格。例如,ADT系统可以将PID、NK1和PV1段拼接到一张表中。但另一方面,在一个单一的表格中包含一个病人的所有就诊历史是很困难的。 3.1.3 显示响应格式 一些情况下,返回的信息不需要被接收的系统保存在数据库里,而只要显示出来就行。 显示响应实际上并不代表组织数据的正式风格。它代表了一个决定,即返回的内容为人类阅读而不是为计算机使用的数据格式。从逻辑上讲,以显示模式返回的内容可能是HL7段模式携带的复杂数据,也可能是由表格模式响应携带的简单记录。 3.2 请求格式 前面介绍的是三种返回查询客户端的方式,现在这里介绍HL7 推荐的三种不同的查询请求方式。 3.2.1 简单参数查询 在简单参数查询中,输入参数在HL7段中连续按顺序传递。 服务器只需要从相应的HL7段中读取它们,并将它们插入到内部函数中执行查询操作。 这是查询的最基本形式,服务端在符合性声明中指定一个固定的参数列表,调用查询时,客户端为每个参数传递一个特定值,这就类似于对数据库调用存储过程并传入参数。 MSH|^~\&|FEH.IVR|HUHA.CSC|HUHA.DEMO||199902031135-0600||QBP^Z58^QBP_Q13|1|D|2.5.1 QPD|Z58^Pat Parm Qry 2|Q502|111069999 RCP|I 3.2.2 示例查询 按示例查询(QBE)是按参数查询(QBP)的扩展,其通过在原本定义的段中发送搜索参数来传递搜索参数,而不是作为QPD段中的字段传递。 例如,如果想要使用QBE执行“查找候选者”查询,则将查询参数保存在PID和或PD 1字段中,并将其中不是查询参数的那些字段留空。 例如,如果宗教不是查询参数之一,则当在查询中发送PID时,PID-17将被留空。 HL 7消息原本定义中不出现的参数,如搜索算法、置信度等, 将继续在QPD段中携带,就像它们在按参数查询一样。 可用作查询参数的确切段和字段将在查询的符合性声明中指定。 MSH|^~\&|FEH.IVR|HUHA.CSC|HUHA.DEMO||199902031135-0600||QBP^Z58^QBP_Q13|1|D|2.5.1 QPD|Z58^Pat Parm Qry 2|Q502 PID|||111069999 RCP|I 3.2.3 选择性查询QSC(Query selection criteria) 第三个方式称为选择性查询QSC,因为它使用了QSC数据类型,而QSC数据类型一般在虚拟表查询中使用。 服务端的符合性声明中将定义客户端可能在表达式中使用的所有变量。 在运行时,客户端能够通过构造类似于“树”节点的方式定义可用的输入参数。 服务端要执行查询,必须可以在运行时分析和解析查询表达式。 服务端可以将输入表达式翻译成它本地可访问数据的语言。 客户端的复杂表达式类似于针对关系数据库的SQL select语句。 MSH|^~\&|FEH.IVR|HUHA.CSC|HUHA.DEMO||199902031135-0600||QBP^Q13^QBP_Q13|1|D|2.5.1 QPD|Z999^Pat Sel Qry 1|Q501|@MedicalRecordNo^EQ^111069999 RCP|I 3.2.4 三种请求格式比较 在使用QSC时,客户端可以选择所提供的任何或所有变量,并且可以为每个变量指定任何允许的运算符和值。 相比之下,在简单参数查询或示例查询中,客户端必须为所提供的所有变量提供值。 简单参数查询易于解析和处理,查询传入参数是预定义好以及有着固定的顺序。 类似地,示例查询也较容易处理,因为参数将出现在定义的段中的固定位置。 相反的,选择性查询需要更多的解析和处理,因为它的灵活性和参数的可选性。 因此,虽然选择性查询向客户端提供了更多功能,但是它对于服务端的处理来说是更繁琐的,简单参数查询和示例查询向客户端提供较少的功能,但通常更易于服务端实现,并且它们往往是基于服务端现有存储过程而提供的。 4 查询返回消息示例 4.1 简单参数查询(QBP)/分段模式返回(RSP) 用户希望查询从1998年5月31日开始到1999年5月31日结束的时间段内,为病历号为“555444222111”的患者分配的所有药物。 使用以下简单参数查询请求消息: MSH|^~\&|PCR|Gen Hosp|PIMS||199811201400-0800||QBP^Z81^QBP_Q11|ACK9901|P|2.5.1|||||||| QPD|Z81^Dispense History^HL7nnnn|Q001|555444222111^^^MPI^MR||19980531|19990531| RCP|I|999^RD| 药房系统识别属于Adam Everyman的医疗记录号“555444222111”,并定位从1998年5月31日开始到1999年5月31日结束的时间段内有4次处方配药,并返回以下RSP消息: MSH|^~\&|PIMS|Gen hosp|PCR||199811201400-0800||RSP^Z82^RSP_Z82|8858|P|2.5.1|||||||| MSA|AA|ACK9901| QAK|Q001|OK|Z81^Dispense History^HL7nnnn|4| QPD|Z81^Dispense History^HL7nnnn|Q001|555444222111^^^MPI^MR||19980531|19990531| PID|||555444222111^^^MPI^MR||Everyman^Adam||19600614|M||C|2222 HOME STREET^^Oakland^CA^94612||^^^^^555^5552004|^^^^^555^5552004|||||34313 2266|||N||||||||| ORC|RE||89968665||||||199805121345-0700|||77^Hippocrates^Harold^H^III^DR^MD||^^^^^555^ 5552104|||||| RXE|1^BID^^19980529|00378112001^Verapamil Hydrochloride 120 mg TAB^NDC|120||mgm|||||||||||||||||||||||||| RXD|1|00378112001^Verapamil Hydrochloride 120 mg TAB^NDC |199805291115-0700|100|||1331665|3||||||||||||||||| RXR|PO|||| ORC|RE||89968665||||||199805291030-0700|||77^Hippocrates^Harold^H^III^DR^MD||^^^^^555^555-5001|||||| RXE|1^^D100^^20020731^^^TAKE 1 TABLET DAILY --GENERIC FOR CALANSR|00182196901^VERAPAMIL HCL ER TAB 180MG ER^NDC |100||180MG|TABLETSA|||G|||0|BC3126631^CHU^Y^L||213220929|0|0|19980821||| RXD|1|00182196901^VERAPAMIL HCL ER TAB 180MG ER^NDC|19980821|100|||213220929|0|TAKE 1 TABLET DAILY --GENERIC FOR CALANSR|||||||||||| RXR|PO|||| ORC|RE||235134037||||||199809221330-0700|||8877^Hippocrates^Harold^H^III^DR^MD||^^^^^555^555-5001||||||RXD|1|00172409660^BACLOFEN 10MG TABS^NDC|199809221415-0700|10|||235134037|5|AS DIRECTED|||||||||||| RXR|PO|||| ORC|RE||235134030||||||199810121030-0700|||77^Hippocrates^Harold^H^III^DR^MD||^^^^^555^555-5001|||||| RXD|1|00054384163^THEOPHYLLINE 80MG/15ML SOLN^NDC|199810121145-0700|10|||235134030|5|AS DIRECTED|||||||||||| RXR|PO 4.2 简单参数查询(QBP)/表格模式返回(RTB) 用户希望获取病历号为“555444222111”的患者的身份信息。使用简单参数查询 MSH|^~\&|PCR|GenHosp|MPI||199811201400-0800||QBP^Z91^QBP_Q13|8699|P|2.5.1|||||||| QPD|Z91^WhoAmI^HL7nnnn|Q0009|555444222111^^^MPI^MR RCP|I|999^RD| RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| 以表格方式返回查询结果: MSH|^~\&|MPI|GenHosp|PCR||199811201400-0800||RTB^Z92^RTB_K13|8699|P|2.5.1|||||||| MSA|AA|8699| QAK|Q0009|OK|Z91^WhoAmI^HL7nnnn|1^1| QPD|Z91^WhoAmI^HL7nnnn|Q0009|555444222111^^MPI^MR RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| RDT|555444222111^^^MPI^MR|Everyman^Adam||19600614|M|| 4.3 简单参数查询(QBP)/显示模式返回(RDY) 用户希望了解从1998年5月31日开始到1999年5月31日结束的时间段内,为病历号为“555444222111”的患者分配的所有药物。请求消息: MSH|^~\&|PCR|Gen Hosp|PIMS||199909171400-0800||QBP^Z97^QBP_Q15|8699|P|2.5.1|||||||| QPD|Z97^DispenseHistoryDisplay^HL7nnnn|Q005|555444222111^^^MPI^MR||19980531|19990531| RCP|I|999^RD| 返回消息: MSH|^~\&|PIMS|Gen Hosp|PCR||199909171401-0800||RDY^Z98^RDY_K15|8858|P|2.5.1|||||||| MSA|AA|8699| QAK|Q005|OK|Z97^DispenseHistoryDisplay|4 QPD|Z97^DispenseHistoryDisplay^HL7nnnn|Q005|555444222111^^^MPI^MR||19980531|19990531| DSP|| GENERAL HOSPITAL – PHARMACY DEPARTMENT DATE:09-17-99 DSP|| DISPENSE HISTORY REPORT Page 1 DSP||MRN Patient Name MEDICATION Dispense DISP-DATE DSP||555444222111 Everyman,Adam VERAPAMIL HCL 120 mg TAB 05/29/1998 DSP||555444222111 Everyman,Adam VERAPAMIL HCL ER TAB 180MG 08/21/1998 DSP||555444222111 Everyman,Adam BACLOFEN 10MG TABS 09/22/1998 DSP||555444222111 Everyman,Adam THEOPHYLLINE 80MG/15ML SOL 10/12/1998 DSP|| << END OF REPORT >> 4.4 示例查询(QBP)/表格模式返回(RTB) 客户希望查看人口统计学资料如下的患者列表: 姓名:张三 性别:男 生日: 1948年12月11日 客户希望使用peekaboo算法,以及满足80%置信水平。 请求消息: MSH|^~\&|PCR|GenHosp|MPI||199811201400-0800||QBP^Z77^QBP_Q13|8699|P|2.5.1|||||||| QPD|Z77^find_candidates^HL7nnnn|Q0001|peekaboo|80| PID|||||张&三||19481211|M RCP|I|25^RD| RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| 返回消息: MSH|^~\&|MPI|GenHosp|PCR||199811201400- 0800||RTB^Z78^RTB_R13|8699|P|2.5.1|||||||| MSA|AA|8699| QAK| QPD|Z77^find_candidates^HL7nnnn|Q0001|peekaboo|80| RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| RDT|555444222111^^^MPI&KP.NCA&L^MR|张^三||19481211|M|| 4.5 选择性查询/表格模式返回(RTB) 用户希望了解从1998年5月31日开始到1999年5月31日结束的时间段内,为病历号为"555444222111"的患者分配的所有药物。 将生成以下消息。 请求消息: MSH|^~\&|PCR|Gen Hosp|PIMS||199811201400-0800||QBP^Z95^QBP_Q13|8699|P|2.5.1|||||||| QPD|Z95^Dispense Information^HL7nnnn|Q504|PID.3^EQ^55544422211^AND~RXD.3^GE^19980531^AND~RXD.3^LE^19990531 RCP|I|999^RD| RDF|3|PatientList^ST^20~PatientName^XPN^48~OrderControlCode^ID^2~OrderingProvider^XCN^120~MedicationDispensed^ST^40~DispenseDate^TS^26~QuantityDispensed^NM^20| 返回消息: MSH|^~\&|PIMS|Gen Hosp|PCR||199811201400-0800||RTB^Z96^RTB_K13|8858|P|2.5.1|||||||| MSA|AA|8699| QAK|Q001|OK|Z95^Dispense Information^HL7nnnn|4 QPD|Z95^Dispense Information^HL7nnnn|Q504|PID.3^EQ^55544422211^AND~RXD.3^GE^19980531^AND~RXD.3^LE^19990531 RDF|3|PatientList^ST^20~PatientName^XPN^48~OrderControlCode^ID^2~OrderingProvider^XCN^120~MedicationDispensed^ST^40~DispenseDate^TS^26~QuantityDispensed^NM^20| RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|77^Hippocrates^Harold^H^III^DR^MD |525440345^Verapamil Hydrochloride 120 mg TAB^NDC |199805291115-0700|100 RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|77^Hippocrates^Harold^H^III^DR^MD |00182196901^VERAPAMIL HCL ER TAB 180MG ER^NDC|19980821-0700|100 RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|88^Seven^Henry^^^DR^MD|00172409660^BACLOFEN 10MG TABS^NDC |199809221415-0700|10 RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|99^Assigned^Amanda^^^DR^MD|00054384163^THEOPHYLLINE 80MG/15ML SOLN^NDC|199810121145-0700|10 5 InterSystems IRIS 对于HL7 V2.x 的支持 5.1 内置 HL7 V2.x 文档 方便随时查看HL7 V2.x 各个字段、节点的含义、限制以及可用字典表定义 可以方便的打开一个HL7 V2.x 文档,鼠标悬停就可以看到该字段的解释: 5.2 互操作性 5.2.1 内置的数据转化工具:使用鼠标拖拽就可以进行数据格式的转换 5.2.2 HL7 消息路由编辑器: 图形化页面设置,方便根据HL7 消息字段内容将消息发送到不同目标 5.2.3 消息追踪器:方便追踪在平台中的经过数据的流向
公告
Claire Zheng · 一月 7, 2021

Global Masters_ Open Exchange 上每个 ZPM 应用程序的奖励积分

亲爱的社区用户,您好! 您可能知道,您在 Open Exchange 上每发布一个应用程序都会获得 [Global Masters](https://intersystems.influitive.com/) 积分奖励。 最近,我们针对 [ZPM](https://openexchange.intersystems.com/package/ObjectScript-Package-Manager) 应用程序推出了附加积分。 **现在,您的每个 ZPM 应用程序都会为您赢得额外的 400 积分!**积分将自动调整。 立即查看 Global Masters 上的积分和可用奖励! 如果您对 Global Masters 有任何疑问,欢迎在下面的评论中提问。 * * * 关于 Global Masters 的其他信息: 什么是 Global Masters? 从这里开始如何在 InterSystems Global Masters 上获得积分  
文章
Michael Lei · 二月 26, 2021

为什么从Cache迁移到IRIS?

不少客户问我关于从Cache迁移到IRIS的问题。为什么要迁移到IRIS?Cache是优秀的,稳定的,有很好的性能,为什么要迁移到IRIS呢?这些客户是对的,但在过去几年,数字化转型提出了不少新问题、新需求和新挑战,客户需要更灵活、更完整、更前瞻的解决方案,InterSystems公司很有远见地洞察到了这一点,推出了IRIS。一句话,IRIS是一套数据平台解决方案,它帮助客户和合作伙伴为迎接数字化转型的挑战提供了充足的弹药。
问题
王喆 👀 · 四月 24, 2021

HealthConnect的启动问题

修改过用户门户之后,重新启动就报这个错,然后使用自带的修复功能,修复之后依然报错,日志中显示没有C:\InterSystems\HealthConnect\mgr\IRIS.WIJ,我复制了别人的过来依然报错,由于代码没有做备份我不能重装,有没有什么办法修复一下,或者把代码备份一下,我再重装。 如果由于修改系统文件、误删除文件等问题导致系统启动、初始化等过程失败,还请联系WRC解决故障。 WRC 联系方式: support@intersystems.com 400-601-9890 https://wrc-china.intersystems.com/wrc/login.csp
公告
Claire Zheng · 三月 7, 2021

如何更好地成为社区贡献者/活跃用户?

亲爱的开发者, 最近我们收到很多类似“如何成为一个活跃(Active)、有贡献值的成员”的问题。 条件很简单! 1. 回答问题 我们有许多未得到回答的问题, 以及 还没有收到有效回答的问题. 非常欢迎您在这些问题上共享您的见解和回答,帮助其他社区成员成长! 2. 撰写文章,发布帖子 撰写您使用InterSystems技术的经验和心得:解决方案、开发心得、解决bug的小技巧、部署等等。 这里是一些 帖子示例 ,这些帖子都对我们的社区建设很有帮助。 除了中文社区外,我们还有英语社区、日语社区、葡语社区等等,我们也欢迎您将这些社区里的文章翻译成中文进行发布! 3. 在Open Exchange提交应用程序 通过Github、Gitlab或任何其他公共存储库,在Open Exchange上共享您的库、解决方案和工具。 如果您对社区有什么问题和建议,欢迎跟帖回复、畅所欲言!我们欢迎大家共同建设社区,使这个社区对中国使用InterSystems技术的开发者们越来越有用! 请新注册的同学们阅读这篇文章,多问问题发帖子/有效评论; 或者多回答问题,中英文都可以: https://cn.community.intersystems.com/?filter=questions; https://community.intersystems.com/?filter=questions