文章
sun yao · 十月 12, 2022 阅读大约需 10 分钟

前端操作自动生成BS、BP、BO

概述

现有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=...

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=...

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=...

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=...

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&LIB...

三、实现方法

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...
image
InterSystems消息查看
image
image
image

五、 结论与猜想

同理,BO也可通过该方法实现自动生成,另可通过建立REST服务或WebService服务的方式通过前端调用该方法实现前端自动生成BS、BP、BO,以简化用户操作,但该方法存在问题点,如BP都为公用单个BP,消息并发量大时可能导致BP堵塞问题,可能实现的解决方法为前端先单独调用接口创建BP,后生成BS,再通过配置实现BS到BP的关联,大家感兴趣可自行尝试,以上,谢谢!

28
5 256
讨论 (10)8
登录或注册以继续

讲解的非常详细,非常有用。

膜拜大佬 学习到了👍👍👍

非常有价值,思路清晰,值得学习

博主的思路值得学习!

谢谢分享,学习了