NCV5新查询模板技术红皮书.docx
《NCV5新查询模板技术红皮书.docx》由会员分享,可在线阅读,更多相关《NCV5新查询模板技术红皮书.docx(20页珍藏版)》请在冰豆网上搜索。
![NCV5新查询模板技术红皮书.docx](https://file1.bdocx.com/fileroot1/2023-1/10/27eff8f3-3872-4d92-8c3d-97b5502281b2/27eff8f3-3872-4d92-8c3d-97b5502281b21.gif)
NCV5新查询模板技术红皮书
新查询模板技术红皮书
NC-UAP5.3
用友NC-UAP
2018-09-23
目录
第一章前言1
第二章设计介绍3
1.模型3
2.默认编辑器及扩展机制4
3.查询模板与数据权限7
第三章开发说明10
1.如何调用10
1.1原先自己实现了查询对话框情况10
1.2没有自己写的查询模板的单据11
1.3常用条件Panel的使用11
1.4多公司的数据权限控制12
2.FAQ12
附录16
1.查询模板中的条件表中的各字段值的意义16
第一章前言
查询模板的功能比较单纯,用一句话概况就是,接受用户的输入,构造SQL语句的Where条件。
另外还可以通过设置固定条件和启用数据权限实现对用户的所能查询的数据做一定的限制。
V53对查询模板重新做了设计(以下称新查询模板)准备逐步替换原来的查询模板(以下称老查询模板)。
老查询模板主要存在问题是1.操作不方便,不够直观,尤其是在定义复杂条件时。
2.无法支持对括号的嵌套,无法支持一些复杂条件,比如Aand(Bor(CandD))
图1老查询模板
新版查询模板较之旧查询模板明显优点 :
(1)新查询模板设计了“普通”和“高级”两个视图,分别针对简单条件和复杂条件的编辑。
普通视图采用Form形式,使简单条件(查询条件都是And联接)的录入更方便。
高级视图采用了类似ClearQuest的树形查询编辑器的形式,避免了括号的输入,使得复杂条件的设置比老查询模板要简单和直观。
图2普通视图
图3高级视图
(2)增加了Between操作符,可以替换各产品组定制的常用条件的“从…到…”类条件。
增强了等于操作符,使其能支持多选(相当把原来的等于和in操作符合成一个了,单独的in操作符仍然支持),这样可以消除实际操作时对“或者(or)”操作符的需要,加强了常规视图的实用性。
第二章设计介绍
1.模型
查询模板最终形成的Where条件总是由若干个相对独立的小条件通过and和or操作符组合合成的。
比如:
部门=’生产部’and(人员编码=’P0001’or人员编码=’P0003’)。
在新查询模板中,这样每个独立的小条件由一个IFilter接口来描述。
IFilter又分成三个组成部分,元信息(IFilterMeta),操作符(IOperator),和字段取值(IFieldValue)。
图1IFilter类结构图
FilterMeta(IFilterMeta的在查询模板中的默认实现)描述了条件字段的元信息,相当于老查询模板的QueryConditionVO。
FilterMeta目前实际就是通过把QueryConditionVO进行转换操作后生成的,以后有可能从单据元数据生成。
IOperator接口是对操作符的抽象。
IFileldValue接口是对字段取值的抽象,它实际只有一个方法,即返回一个IFieldValueElement的集合。
IFieldValueElement是对一个“原子”值的抽象,实际可能是描述了一个整数,或者是一个枚举值,或者是参照返回值。
整个的Where条件就可以看成由and,or操作符以及IFilter形成的一棵树,在新查询模板中以QueryTree来描述。
前面举的例子,表示成树的形式就如下图所示。
图2Where条件树
不论是高级视图还是常规视图,其内部都是用QueryTree来组织单个的IFilter的。
QueryTree及相关类结构图如图3所示
图3QueryTree相关类结构图
2.默认编辑器及扩展机制
基于前面的模型,查询模板从功能上就可以理解为一个是能对QueryTree和IFilter进行编辑的编辑器。
对QueryTree的编辑实际就是把IFilter看成最小的单元,提供一定的方法,让用户修改IFilter之间的与或关系。
对于常规视图来说,用户不用关心,实际也不能修改,IFilter之间的与或关系。
常规视图上的IFitler都是“与(AND)”的关系,用户不能修改,只能增加或删除IFilter。
对于高级视图,用户可以则可以任意调整IFilter之间的与或关系,从而构造出复杂的查询条件。
新查询模板对QueryTree的编辑没有设计扩展机制,因为对与或关系的编辑没有太多的特殊性需求,常规视图和高级视图已经能完全满足简单条件和复杂的条件的编辑了。
但是对于IFilter的编辑,往往是和具体的业务相关的,默认的编辑方式不一定能满足业务的要求。
因此新查询模板针对这个问题重点做了设计,支持在不同层次上对IFilter编辑进行扩展。
对IFilter进行编辑的编辑器被抽象为接口IFilterEditor,当查询模板需要一个IFilter的编辑器,即一个IFilterEditor实例时,会调用FilterEditorManager创建出一个。
默认的情况下查询模板创建出来的是DefaultFilterEditor,如图4所示。
如果DefaultFilterEditor不能满足业务的需要,则可以按需定制符合要求的编辑器,并注册到FilterEditorManager中,即可实现对DefaultFilterEditor的替换。
图5展示了一个定制的FilterEditor,对于“经济类型”这样的枚举型字段,采用图示的编辑器可能更方便。
图4DeafultFilterEditor
图5定制的FilterEditor
图4中被红色线框框住的部分即是DefaultFilterEditor,它由三部分组成:
左边是显示FilterMeta的Label控件,中间是供选择操作符的下拉框控件,右边被绿色线框框住的部分是对IFieldValue进行编辑的编辑器,抽象为IFieldValueEditor,默认情况下创建出来的是DefaultFieldValueEditor,同DefaultFilterEditor一样,通过FieldValueEditorManager注册定制的编辑器,DefaultFieldValueEditor也是可以被替换的。
如前文所述,IFieldValue是对IFieldValueElement包装,而DefaultFieldValueEditor实际上是对IFieldValueElementEditor的包装。
DefaultFieldValueEditor包含了两个IFieldValueElementEditor的实例,根据用户所选择的操作符的不同会有不同的界面展现。
图4中选择的是“介于”操作符,它是一个接受2个参数的操作符,因此DefaultFieldValueEditor把两个IFieldValueElementEditor都显示出来了。
如果是其他情况,即操作符是接受0个,1个,任意多个参数时,DefaultFieldValueEditor总是显示一个IFieldValueElementEditor,但会根据操作符接受参数的个数,设置IFieldValueElementEditor是否允许多选。
上面所描述的DefaultFieldValueEditor的行为如果符合业务的需要,那么你可以坦然接受它,反之则是定制你自己的IFieldValueEditor的时候了。
在没替换DefaultFieldValueEditor的情况下,用户在为某个查询条件选定一个部门,或者一个公司,或者一个人员,或者输入一个订单货品的数量,实际都是通过IFieldValueElementEditor进行的,而后者是通过IFieldValueElementEditorManager创建出来的,默认的情况下它又把这个任务委托给了DefaultFieldValueElementEditorFactory这个工厂类。
这个工厂类根据注册在pub_query_condition的信息,主要是字段的数据类型等信息,生产出合适的IFieldValueElementEditor实例。
同样地,这个过程也是可以根据需要扩展的,支持注册定制的IFieldValueElementEditor。
综上所述,新查询模板对IFilter的编辑,分别在IFilterEditor,IFieldValueEditor,IFieldValueElementEditor三个层次上提供了扩展机制,在实际使用时可以根据情况具体选择扩展方式。
图6是相关的类图,从中可以看到,这三个层次的扩展机制是完全相同的。
都是由一个IXXEditor接口来表示编辑器,然后有一个IXXEditorFactory的工厂来负责创建编辑器,一个XXEditorManager来管理工厂。
注册一个定制的编辑器,概况来讲可以分成三个步骤,1.提供一个实现了IXXEditor接口的自定义编辑器2.提供一个相应的工厂类,负责生产该自定义编辑器3.把该工厂类注册到XXEditorManager中。
关于如何注册,请参看开发说明有关章节!
图6IFilter编辑器相关类图
3.查询模板与数据权限
NC中需要控制数据权限的业务节点,对于查询操作的控制,即限制用户仅能查询到其有权限的数据,一般是通过查询模板来实现的。
其原理是由查询模板在用户设置的查询条件后面自动附件数据权限相关的条件。
举例来说,订单上有经销商(custom),存货(inv)等字段。
现在需要限制某个用户仅能查询特定的经销商和特定的存货订单。
用户A,其有权限的经销商为C1,C2,C3,其有权限的存货为I1,I2,I3。
那么当用户A对订单执行查询操作时,不论其设置的条件是什么查询模板都必须附加如下的SQL语句。
(customisnullorcustomin('C1','C2','C3'))
and
(invisnullorcustomin('I1','I2','I3))
当然对于订单来说,其经销商和存货字段一般都是不可为空的,上面的"customisnull"这样的条件有些多余,但如果对于其他可以为空的字段则必须加上这样的条件,否则则查询出来的结果可能是不完全的。
另外实际查询模板拼接出来的sql不会象上面一样是用('C1','C2','C3')这样的形式枚举其有权限的数据而是一个有权限服务提供一个子查询sql语句。
实际的sql是以下的形式
bd_cubasdoc.pk_areaclisnull
OR
bd_cubasdoc.pk_areaclin
(selectdistinctpower.resource_data_id
from
sm_power_areaclasspower,sm_useru,
sm_user_roleurole
where
u.cuserid='0001SD100000000000UP'and
u.cuserid=urole.cuseridand
urole.pk_corp='1001'and
urole.pk_role=power.pk_roleand
power.orgtypecode=1and
(power.pk_org='1001'oriscommon_powerin('y','Y')
)
NC的权限不但和角色相关,和角色所属的组织也相关,即权限实际上描述了,角色,组织和资源之间的关系。
在上面的例子里,忽略了多个组织的情况。
因为一般情况下对一个单据的查询实际上有个隐含的限制条件,即查询当前角色所登录的组织(公司或者主体账簿)的单据。
当要需要对多个组织的数据进行查询时,则数据权限的附件条件要比上面复杂许多。
还是上面的例子,现在需要指出订单上还有一个字公司(pk_corp)字段,即表示订单是按照公司的组织维度来区分的。
现在用户A有权限查询的公司为(O1,O2),并且对于公司o1,其有权限的经销商为(C1,C2),有权限的存货为(I1,I2);对于公司o2,其有权限的经销商为(C2,C3),有权限的存货为(I2,I3)。
那么用户A对公司o1和o2的数据进行查询时,需要附加的数据权限相关条件如下:
(
(pk_corp='o1'
and
(customisnullorcustomin('C1','C2'))
and
(invisnullorinvin('I1','I2'))
)
or
(pk_corp='o2'
and
(customisnullorcustomin('C2','C3'))
and
(invisnullorinvin('I2','I3'))
)
)
同样上面的语句只是示意,实际的过程是对于每个公司都需要向权限服务获取字段custom和inv对应的数据权限子查询,然后组合成最终的条件。
关于数据权限具体,参见开发说明之多公司的数据权限控制
第三章开发说明
1.如何调用
1.1原先自己实现了查询对话框情况
旧的方式
对应的新的方式
nc.ui.pub.query.QueryConditionClient(含引用和继承)
getUIPanelNormal():
得到常用条件Panel
.getWhereSQL():
获得查询对话框的SQL
setNormalShow;hideNormal():
可见NormalPanel
setShowDefine;hideDefine:
可见用户自定义Panel
nc.ui.querytemplate.QueryConditionDLG
getNormalPanel
getWhereSQL
SetVisibleNormalPanel(boolean)
SetVisibleUserDefPanel(boolean)
setTemplateID(..)
废弃,如果需要指定的templateid,只需要在传入的参数templateinfo中设置其templateid即可;如果确实外面想在show之前自己加载数据,可以调用方法initUIData
代码样例:
(A)继承查询对话框样例代码可参见:
nc.ui.trade.query.HYQueryConditionDLG该类只适用于UI工厂,产品组请勿继承和调用之,且参照之即可。
(B)直接调用样例代码示例:
TemplateInfotempinfo=newTemplateInfo();
tempinfo.setPk_Org(ClientEnvironment.getInstance().getCorporation()
.getPrimaryKey());
tempinfo.setCurrentCorpPk(ClientEnvironment.getInstance().getCorporation()
.getPrimaryKey());
tempinfo.setFunNode(getModuleCode());
tempinfo.setUserid(ClientEnvironment.getInstance().getUser()
.getPrimaryKey());
queryDlg=newQueryConditionDLG(this,null,tempinfo);
queryDlg.showModal();
1.2没有自己写的查询模板的单据
采用与UI工厂原先类似的方法。
只是把
nc.ui.trade.query.HYQueryDLG
替换为
nc.ui.trade.query.HYQueryConditionDLG.
一般用UI工厂做的查询模板不需要考虑这个!
1.3常用条件Panel的使用
1.简单得到并操作.
常用条件Panel必须实现接口nc.ui.querytemplate.normalpanel.INormalQueryPanel。
把常用条件Panel放上去有两方式:
(1)如果没有逻辑条件,直接通过QueryConditionDLG的构造子传进去。
(2)对于可能有的逻辑条件参见FAQ如何组织非查询条件
现在只有一种方式:
通过自己的构造子传入。
对于逻辑条件的处理亦参见FAQ如何组织非查询条件(有修正)
的得到
2.持久化保存问题
如果要把对应的常用条件保存到我的收藏夹(即查询模板的个人历史保存)。
则必须实现方法setNormalQueryObject和getNormalQueryObject。
反之donothing.
序列化的对象和字段不能为transient
样例类如下:
privateclassTestNormalPanelextendsUIPanelimplementsINormalQueryPanel{
privateUILabellabel=newUILabel("Fillthetxt:
");
privateUITextFieldtxtField=newUITextField(30);
publicTestNormalPanel(){
super();
add(label);
add(txtField);
}
publicJComponentgetComponent(){
returnthis;
}
publicObjectgetNormalQueryObject(){
returntxtField.getText();
}
publicvoidsetNormalQueryObject(ObjectqryObject){
txtField.setText(qryObject==null?
"":
String.valueOf(qryObject));
}
}
3.默认情况下常用条件panel是不会显示的,所以欲显示之,则必须调用setVisibleNormalPanel(true).--含数据库级的逻辑条件的现实
现在是不需要主动调用setVisibleNormalPanle.因为平台是根据构造子里面传入的INormalPanel是否为空,来添加与否。
对于逻辑条件Panel,默认有就增加,没有也就不处理。
(如果想自己处理逻辑条件)。
1.4多公司的数据权限控制
1.单字段---即所有权限字段依赖于单一的字段(一对多关系)
2.多字段---即不同的权限字段可能依赖于不同的字段(多对多关系)
(一)程序调用方法:
在初始化查询界面时候设置之。
(即在TemplateInfo中设置之)
//step1:
设置权限组织字段
PowerCorrelationVOpcvo=newPowerCorrelationVO();
pcvo.setOrgFieldcode("bd_cubasdoc.pk_corp1");
//step2:
设置被关联的字段
pcvo.addCorrelatedFieldcode("bd_cumandoc.pk_resppsn1");
pcvo.addCorrelatedFieldcode("bd_cumandoc.pk_respdept1");
templateinfo.addPowerCorrlationVO(pcvo);
//
多字段PowerCorrelationVOpcvo2=newPowerCorrelationVO();
……(同上)
templateinfo.addPowerCorrlationVO(pcvo2);
//调用查询界面构造子
2.FAQ
关于废弃的旧的接口方法,如何又新的方法来实现原来的功能?
有些新的功能如何实现?
FAQ列表!
1.如何定制某个Field编辑后的响应事件?
即原来的afterEditor方法?
答:
可以参照nc.ui.querytemplate.filtereditor.MultiCorpFilterEditor来实现一个Editor(继承DefaultFilterEditor,并注册对应的监听即可!
),然后注册作为FilterEditorFactory注册到查询UI中。
注意其不能触发别的FilterEditor,只能触发自己本身!
如果想联动触发,参见FAQ如何设置组合Filter
2.如何定制自己的编辑器,如非默认的“男女”的下拉框?
答:
这和FAQ3问题是同一性质,可以只不过返回的FieldValueElementEditor不同而已
UIComboBoxcombo=newUIComboBox();
combo.addItem(null);
combo.addItem(newDefaultConstEnum(UFBoolean.TRUE,”男”);//TODOi18n
combo.addItem(newDefaultConstEnum(UFBoolean.FALSE,”女”);//i18n
e=newDefaultFieldValueElementEditor(combo);
3.如何给特定的查询的参照,设置过滤条件?
答:
可以参照nc.ui.querytemplate.QueryConditionEditor,main方法中注册“部门档案”的方法去注册一个registerFieldValueEelementEditorFactory即可。
在其中可以自己去设置条件。
示例代码如下
m_queryDlg.registerFieldValueEelementEditorFactory(
newIFieldValueElementEditorFactory(){
publicIFieldValueElementEditorcreateFieldValueElementEditor(FilterMetameta){
if("pub_systemplate.operator".equals(meta.getFieldCode())){
UIRefPanerefPane=newUIRefpaneCreator(m_queryDlg.getQueryContext()).createUIRefPane(meta);
refPane.setWhereString("pk_corp='"+getCurrPK_corp()+"'");
returnnewRefElementEditor(refPane,meta.getReturnType());
}
returnnull;
}
});
4,如何在程序中动态地处理查询条件呢?
答:
其可能应用的有两种情况。
(1)在数据库中模板数据基础上.过滤,修改(如自定义项的处理),增加查询条件。
需要注册一个模板VO处理器。
然后具体调用示例代码
qryDlg.registerQueryTemplateTotalVOProceeor(newIQueryTemplateTotalVOProcessor(){
publicvoidprocessQueryTempletTotalVO(QueryTempletTotalVOtotalVO){
//here,dowhatyouwanttodo.Note:
totalVOisareference
}