#业务服务

0 关注者 · 8 帖子

业务服务是 InterSystems Ensemble 互操作性产品的一部分,负责接受来自外部应用程序的请求。

文章 Qiao Peng · 一月 31, 2024 21m read

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也是以这些技术选项为基础的。

0
1 274
文章 Qiao Peng · 十二月 7, 2023 5m read

TCP作为OSI 7层的传输层的通信协议,其使用上不像更上层的通信协议那么方便,因为TCP操作的不是数据包,它操作的是数据流。因此有多种将TCP数据流“解释”为数据包(消息)的方法。

InterSystems IRIS提供了多种TCP适配器,用于不同的“解释”,例如EnsLib.TCP.FramedInboundAdapter使用特定的首尾字符做为分隔、EnsLib.TCP.CountedInboundAdapter使用固定的长度进行分隔...

同时,InterSystems IRIS提供了多种开箱即用的TCP业务服务和业务操作,方便接入和发送TCP数据。这里我们介绍常见的使用特定的首尾字符做为分隔的TCP业务服务和业务操作。

1. 通用TCP业务服务和业务操作

EnsLib.TCP.Framed.PassthroughService和EnsLib.TCP.Framed.PassthroughOperation是一组使用特定的首尾字符做为分隔TCP数据流的通用业务服务和业务操作。EnsLib.TCP.Framed.PassthroughService业务服务会将TCP数据封装在Ens.StreamContainer发送给业务流程或业务操作;而EnsLib.TCP.Framed.PassthroughOperation业务操作发送并接收Ens.StreamContainer类型的数据。

0
0 222
文章 Qiao Peng · 十二月 4, 2023 10m read

1. 通用RESTful业务服务和业务操作


InterSystems IRIS 提供了一组通用的RESTful 业务服务和业务操作类,用户无需开发自定义的业务服务和业务操作类,就可以直接向外提供RESTful服务和调用外部的RESTful API。

BS EnsLib.REST.GenericService 通用REST业务服务
BS EnsLib.REST.SAMLGenericService 检查SAML令牌的签名和时间戳的REST业务服务
BO EnsLib.REST.GenericOperation 通用REST业务操作
BO EnsLib.REST.GenericOperationInProc 用于透传模式的通用REST业务操作

2. 通用RESTful 消息

通用的RESTful 业务服务和业务操作类使用一个通用的RESTful消息类 - EnsLib.REST.GenericMessage,它是EnsLib.HTTP.GenericMessage的子类,二者数据结构都是

0
2 227
文章 Vincent Wu · 十月 23, 2022 4m read

   TrakCare LabTrakCare品中,属于检验用的系统,也就是LIS(Laboratory Information System)LIS在实室中主要处理检验以下的作业:收、采血管卷打印、检验仪器联机、仪器果接收、仪器据判验证危险值通知与警示等多作业,TrakCare Lab也同时将流程间的运作,串联得相当规律且正确,使得医检师们得以有条不紊的工作。

检验报告核发的正确性在于一始的本采集及采血管的正确使用,若一始便贴错病患或是用采血管,不让检验报冠李戴外,错误的采血管亦让检验数据有所偏差,嘱错误发生病安事件,故本正确采集的重要性可想而知。

    病患大部份是直接到检验科采,在业医检师TrakCare Lab核机制及自动备管机的运用之下,出率相。据床上统贴错病患标签采血管错误住院或急,故降低检验科外部在采上的错误率便成为开发护理站自动备管采系统(NSAD)的主要目的。

    们试着延伸TrakCare Lab机制至医院的护理站,在与客户商后,决定同从硬件及件两方面双管齐下:

硬件方面:

0
0 137
文章 Qiao Peng · 十月 22, 2022 4m read

1. 新的系统SQL业务服务/业务操作

接连SQL数据源和操作SQL数据目标是常见的集成业务场景。使用SQL适配器监控SQL数据源和操作SQL目标库时,我们需要开发自定义BS或BO,写不少代码。例如开发自定义SQL服务需要:

1. 开发响应消息类,用于承接SQL快照数据;

2. 开发自定义业务服务BS类,用于将SQL快照按字段赋值给对应的消息,并将消息发送给目标(业务流程或业务操作)。

而要开发自定义SQL操作,更麻烦些:

1. 开发请求和响应消息类,用于向BO传输数据和接收返回数据;

2. 开发自定义业务服务BO类,设置消息响应表,根据不同请求消息类型编写方法;

3. 在方法中根据请求消息数据拼写SQL语句;

4. 在方法中将SQL执行结果存入响应消息。

虽然很简单,但编程过程枯燥乏味。而且当修改SQL语句时,还要修改对应的消息类和BS/BO类。

从2021.2开始,InterSystems IRIS增加了2套系统通用SQL业务服务和SQL业务操作:

0
1 638
文章 sun yao · 十月 12, 2022 10m read

概述

现有Ensemble平台BS(服务)、BP(流程)、BO(操作)需对平台及开发语言有一定的了解才能实现,为简化用户操作,现对现有平台进行二次封装,通过API接口的形式进行前后端分离,通过前端界面操作实现BS(对外提供的服务)、BP、BO(逻辑处理或调用外部的服务)自动生成(通过%Dictionary实现),具体实现如下。

一、开发技术和工具

版本:Ensemble 2017.2.1

二、涉及公用类

2.1 %Dictionary.ClassDefinition(自定义类)

• property Super as %CacheString; Specifies one or more superclasses for the class. 定义一个或多个父类,继承父类

• property** ProcedureBlock** as %Boolean [ InitialExpression = 0 ]; Specifies that the class uses procedure block for method code. 设置类是否允许使用程序块,程序块强制实施变量作用域:方法无法看到由其调用方定义的变量,程序块中的任何变量都会自动成为私有变量

• relationship Parameters as %Dictionary.ParameterDefinition [ Inverse = parent,Cardinality = children ]; Parameter. 定义类参数,如全局变量、适配器等相关定义

• relationship Methods as %Dictionary.MethodDefinition [ Inverse = parent,Cardinality = children ]; Method. 定义类方法

参考链接:http://localhost:57772/csp/documatic/%25CSP.Documatic.cls?APP=1&LIBRARY=%25SYS&CLASSNAME=%25Dictionary.ClassDefinition

2.2 %Dictionary.ParameterDefinition(自定义类参数)

• property Name as %Dictionary.CacheIdentifier [ Required ]; The name of the parameter. 定义参数名

• property Default as %CacheString [ SqlFieldName = _Default ]; Specifies a default value for the parameter assuming the Expression keyword is blank. 定义参数默认值,不设置则为空

• property Description as %CacheString; Specifies a description of the parameter. 定义参数描述

参考链接:http://localhost:57772/csp/documatic/%25CSP.Documatic.cls?APP=1&LIBRARY=%25SYS&CLASSNAME=%25Dictionary.ParameterDefinition

###2.3 %Dictionary.MethodDefinition(自定义类方法) • property Name as %Dictionary.CacheIdentifier [ Required ]; The name of the method. 定义方法名

• property ClassMethod as %Boolean [ InitialExpression = 0 ]; Specifies that the method is a class method. Instance methods can only be invoked via an instantiated object while class methods can be directly invoked without an object instance. 指定该方法是类方法。实例方法只能通过实例化的对象调用,而类方法可以在没有对象实例的情况下直接调用。

• property FormalSpec as %CacheString; Specifies the list of arguments. Each argument is of the format [&|*][:][=] where & means pass-by-reference and * means output-only. 定义方法入参,每个入参格式为“参数名:参数类型=默认值”,如:code:%String=””

• property ReturnType as %Dictionary.CacheClassname; Specifies the data type of the value returned by a call to the method. Setting ReturnType to an empty string specifies that there is no return value. 定义方法返回值,设置为空则无返回值

• property WebMethod as %Boolean [ InitialExpression = 0 ]; Specifies that a method can be invoked as a web method using the SOAP protocol. 设置方法是否为web方法,适用于SOAP协议

• property Implementation as %Stream.TmpCharacter; The code that is executed when the method is invoked. In the case of an expression method, this is an expression. In the case of a call method, this is the name of a Cache routine to call. 调用方法时执行的代码。对于表达式方法,这是一个表达式。对于调用方法,这是要调用的缓存例程的名称

参考链接:http://localhost:57772/csp/documatic/%25CSP.Documatic.cls?APP=1&LIBRARY=%25SYS&CLASSNAME=%25Dictionary.MethodDefinition

###2.4 Ens.Config.Production • property Items as list of Ens.Config.Item(XMLNAME="Item",XMLPROJECTION="ELEMENT"); 定义Production下的BS、BP、BO,根据父类确认属于哪一类

• method SaveToClass(pItem As Ens.Config.Item = $$$NULLOREF) as %Status This method saves the production into the XData of the corresponding class

参考链接: http://localhost:57772/csp/documatic/%25CSP.Documatic.cls?APP=1&LIBRARY=ENSLIB&CLASSNAME=Ens.Config.Production

###2.5 Ens.Config.Item(BS服务、BP流程、BO操作) • property PoolSize as %Integer(MINVAL=0,XMLPROJECTION="ATTRIBUTE"); Number of jobs to start for this config item. Default value: 0 for Business Processes (i.e. use shared Actor Pool) 1 for FIFO message router Business Processes (i.e. use a dedicated job) 1 for Business Operations 0 for adapterless Business Services 1 for others For TCP based Services with JobPerConnection=1, this value is used to limit the number of connection jobs if its value is greater than 1. A value of 0 or 1 places no limit on the number of connection jobs. 设置缓冲池大小

• property Name as %String(MAXLEN=128,XMLPROJECTION="ATTRIBUTE") [ Required ]; The name of this config item. Default is the class name. 设置BS、BP、BO名称

• property ClassName as %String(MAXLEN=128,XMLPROJECTION="ATTRIBUTE") [ Required ]; Class name of this config item. 设置BS、BP、BO类名称

• property Category as %String(MAXLEN=2500,XMLPROJECTION="ATTRIBUTE"); Optional list of categories this item belongs to, comma-separated. This is only used for display purposes and does not affect the behavior of this item. 设置类别 • property Comment as %String(MAXLEN=512,XMLPROJECTION="ATTRIBUTE"); Optional comment text for this component. 设置注释

• property Enabled as %Boolean(XMLPROJECTION="ATTRIBUTE") [ InitialExpression = 1 ]; Whether this config item is enabled or not. 设置启用停用标志

参考链接:http://localhost:57772/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=ENSLIB&CLASSNAME=Ens.Config.Item ##三、实现方法 ###3.1 创建BS模板类 创建模板类,后续类生成方法体通过模板类获取

/// BS的SOAP模板
Class HIP.Platform.Template.BSSOAPTemplate Extends EnsLib.SOAP.Service
{

Parameter ADAPTER;

Parameter NAMESPACE = "http://tempuri.org";

Parameter SERVICENAME = "BSSOAPTemplate";

Method TemplateFun(code As %String, data As %GlobalCharacterStream) As %GlobalCharacterStream [ WebMethod ]
{
	set OutStream=##class(%GlobalCharacterStream).%New()
	try{
		s ..%ConfigName = $classname($this)
		set sourceCode=$p($classname($this),".",4) //PUB000X
		set methodCode=##safeexpression(""""_$get(%methodname)_"""") //SendDataFromHis
		
		s messageCode = $p(code,"^",1)
		s requestType= $select($p(code,"^",2)="REST":"REST", 1:"SOAP")
		set proc = ##class(%SYS.ProcessQuery).%OpenId($j) //当前进程 获取调用服务客户端的IP地址
		
		set sc = ##class(HIP.Service.PublishService).GetAllowedIP(sourceCode)
		if +sc=1 {
			s allowedIP = $p(sc,"^",2)
			if allowedIP '[ proc.ClientIPAddress {
				SET oref=##class(%Exception.General).%New("<401>","无权限",,"您的IP地址不允许访问,请联系管理员") 
         		THROW oref
			}
		}else{
			return sc
		}
		s request = ##class(HIP.Platform.Message.Request).%New()
		s request.sourceCode=sourceCode		//PUB0001
		s request.requestType=requestType	//REST SOAP
		s request.inputFlag="0"				//-1表示失败,0表示未处理,1表示成功
		s request.inputStream = data		//JSON流,或者XML流
		s request.messageCode=messageCode	//BOE0001
		
		Set tSC=..SendRequestSync("HIP.Platform.BP.ProcessCode",request,.pOutput)
		If $$$ISERR(tSC) Do ..ReturnMethodStatusFault(tSC)
		
		d OutStream.CopyFrom(pOutput.outStream)
		return OutStream
	}catch err {
		set OutStream=##class(%GlobalCharacterStream).%New()
		do OutStream.Write(err.DisplayString())
		return OutStream
	}
}

Storage Default
{
<Data name="BSSOAPTemplateDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
</Data>
<DataLocation>^HIP.PlatforE240.BSSOAPTemplateD</DataLocation>
<DefaultData>BSSOAPTemplateDefaultData</DefaultData>
<IdLocation>^HIP.PlatforE240.BSSOAPTemplateD</IdLocation>
<IndexLocation>^HIP.PlatforE240.BSSOAPTemplateI</IndexLocation>
<StreamLocation>^HIP.PlatforE240.BSSOAPTemplateS</StreamLocation>
<Type>%Library.CacheStorage</Type>
}

}

###3.2 自动生成BS,并添加至Production中 通过模板类自动生成WebService方法,并添加到Production的BS中

/// 创建BS服务 PUB00XX服务,提供给第三方调用
/// d ##class(HIP.Util.SOAP).BSCreateSOAPInfo("PUB0001","提供给HIS访问平台")
ClassMethod BSCreateSOAPInfo(Code As %String, Desc As %String) As %Status
{
	///HIP.Platform.BS.PUB0001
	s src = "HIP.Platform.BS."_Code_".PublishWebService"
	s isExist = 0
	try {
		
		set isExist=##class(%Dictionary.ClassDefinition).%ExistsId(src)
		if isExist=1 { //类已存在则更新,先删除再插入
			set classObj = ##class(%Dictionary.ClassDefinition).%OpenId(src)
			d classObj.Parameters.Clear()
			d classObj.Properties.Clear()
			d classObj.Indices.Clear()
			d classObj.ForeignKeys.Clear()
			d classObj.Methods.Clear()
		}else { //类不存在则新建
			set classObj = ##class(%Dictionary.ClassDefinition).%New(src)
		}
		//设置父类
		s classObj.Super="EnsLib.SOAP.Service"
		//设置允许使用程序块,则可动态定义变量
		s classObj.ProcedureBlock=1

		///Parameter的值
		//设置适配器
		set ParDef = ##class(%Dictionary.ParameterDefinition).%New()
		set ParDef.Name="ADAPTER"
		d classObj.Parameters.Insert(ParDef)
		set ParDef = ##class(%Dictionary.ParameterDefinition).%New()
		//设置服务名
		set ParDef.Name="SERVICENAME"
		set ParDef.Default=Code
		set ParDef.Description=Desc
		d classObj.Parameters.Insert(ParDef)
		//设置命名空间
		set ParDef = ##class(%Dictionary.ParameterDefinition).%New()
		set ParDef.Name="NAMESPACE"
		set ParDef.Default="www.boe.com"
		d classObj.Parameters.Insert(ParDef)
		
		///函数模板代码,通过模板类获取
		s methodTemplate = ##class(%Dictionary.MethodDefinition).%OpenId("HIP.Platform.Template.BSSOAPTemplate||TemplateFun")
		Set methodObj=##class(%Dictionary.MethodDefinition).%OpenId(src_"||SendData")
		if methodObj="" Set methodObj=##class(%Dictionary.MethodDefinition).%New(src_".SendData")
		//设置方法名
		set methodObj.Name="SendData"
		set methodObj.ClassMethod=0
		//set methodObj.FormalSpec="code:%String,data:%GlobalCharacterStream,*pOutput:HIP.Platform.Message.Response"
		//设置方法入参
		set methodObj.FormalSpec="code:%String,data:%GlobalCharacterStream"
		//设置方法返回值
		set methodObj.ReturnType="%GlobalCharacterStream"
		//设置方法为WebService方法
		set methodObj.WebMethod=1
		//设置方法具体实现代码,通过模板类获取
		set methodObj.Implementation=methodTemplate.Implementation
		d classObj.Methods.Insert(methodObj)
		
		set sc=classObj.%Save()
		if $$$ISERR(sc) {
			return $system.Status.GetErrorText(sc)
		}else{
			d $system.OBJ.Compile(src,"ck/displaylog=0")
		}
		if isExist=0 {
			//存储到production中
			s prodObj = ##class(Ens.Config.Production).%OpenId("HIP.Platform.Production")
			if $IsObject($G(prodObj)){
				Set item = ##class(Ens.Config.Item).%New()
				Set item.PoolSize = 1 
				Set item.Name = src
				Set item.ClassName = src
				Set:item.Name="" item.Name = item.ClassName
				Set item.Category = ""
				Set item.Comment = Desc
				Set item.Enabled = 1
				Set tSC = prodObj.Items.Insert(item)
				
				If $$$ISOK(tSC) {
					// save production (and item)
					Set tSC = prodObj.%Save()
					set ^TempSy("tSC")=tSC
					If ($$$ISOK(tSC)) {
						// update production class
						Set tSC = prodObj.SaveToClass()
					}
					return tSC
				}
				If $$$ISERR(tSC) return $system.Status.GetErrorText(tSC)
			}
		}
		return $$$OK
	} catch(ex) {
		return ex.DisplayString()
	}
}

###四、 结果展示 运行 d ##class(HIP.Util.SOAP).BSCreateSOAPInfo("PUB0001","提供给HIS访问平台") 后,Studio中自动生成HIP.Platform.BS.PUB0001.PublishWebService.cls 类 如下: image 打开Portal管理界面,Production配置,可看到该服务已添加至Production中,如下: image 可直接通过soapUI调用,地址 http://localhost:57772/csp/hip/HIP.Platform.BS.PUB0001.PublishWebService.CLS?WSDL=1image InterSystems消息查看 imageimageimage ###五、 结论与猜想 同理,BO也可通过该方法实现自动生成,另可通过建立REST服务或WebService服务的方式通过前端调用该方法实现前端自动生成BS、BP、BO,以简化用户操作,但该方法存在问题点,如BP都为公用单个BP,消息并发量大时可能导致BP堵塞问题,可能实现的解决方法为前端先单独调用接口创建BP,后生成BS,再通过配置实现BS到BP的关联,大家感兴趣可自行尝试,以上,谢谢!

10
6 789
文章 Michael Lei · 六月 9, 2022 2m read

业务服务Business Service/BS是能够支持我们从外部数据来源获取数据强大的组件,在在大多数情况下,内置的现成组件就已经可以完成这项工作,但有时候我们还是需要写编码来自定义业务服务。在这样做的时候,有一些最佳实践供大家参考。

 
  1. 精益求精--业务服务的代码应该是最小化处理。这是由于如果在业务服务中发生任何错误,将不发送任何消息,从而将不创建任何跟踪。这使得它很难进行故障排除。相反,要尽可能快地完成Ensemble消息,并将其传递给适当的目标。有些人认为,如果有一个流进来,Ensemble消息应该包括一个流属性,然后由一个业务流程来解析这个流。(见文档中的例子3)还有人说,只要包括良好的错误检查,最小的处理就可以了。在这两种情况下,请记住,数据转换是存在的,可以在业务流程中使用,以便对该数据做进一步的翻译。这些转换不应该被从业务服务中调用。(参见文档中的示例1)。
  2. 可配置的目标:--在大多数情况下,业务服务的代码应该是一个可配置的目标,而不是将其硬编码到业务服务中。要做到这一点,你可以在业务服务类中创建一个名为TargetConfigNames的属性,其类型为Ens.DataType.ConfigName,使用SETTINGS参数并将此设置放在配置页上。
0
0 204