页面树结构
转至元数据结尾
转至元数据起始

1 概述

    需求:
    (1)平台集成:即实现单点登录,完成OA系统用户及组织架构信息同步到Smartbi系统,通过OA登录后访问报表平台系统不需要重新登录。
    (2)报表集成:支持通过URL链接的方式在OA系统集成报表平台相关报表、图表等资源后,根据当前OA用户可以直接访问有权限的报表。
    在本示例需求中,Smartbi与泛微OA的集成主要涉及用户同步、单点登录、报表集成。

 

 

 

2 用户同步

    本次用户同步采用计划任务调用smartbi接口的方式进行。通过直接数据库读取的方式,获取第三方的用户信息和机构信息,注:本次示例中,假定权限全部在Smartbi中配置,故不同步角色信息。

2.1 同步逻辑:

     用户同步依据用户名(工号)作为唯一标识,机构同步依据机构ID作为唯一标识。泛微OA的用户密码采用MD5加密为大写的加密串,如密码:"123",泛微OA中存储的加密串为:202CB962AC59075B964B07152D234B70;而在Smartbi中存储的加密串为:0202cb962ac59075b964b07152d234b70。因而,在同步时,只需将泛微OA的密码转换成小写,在密码前加:"0",然后再存入Smartbi中,即可直接使用泛微OA的密码登录Smartbi,也即是连同密码也同步到了Smartbi。
    OA的用户和机构都存在是否有效的标识,1表示用户或机构有效,0表示无效。在同步的时候会先判断这个标识:
      (1)标识为1,则进行同步,同步时,如果之前已经同步到BI,则判断OA中的信息与BI中的信息是否一致,一致则不再重复同步,不一致则以OA中的信息为准更新BI中的信息。
      (2)标识为0,则判断是否之前已经同步到BI,如果已经同步,则进行删除,如果没有同步则跳过,不作同步处理。

2.2 部署配置

2.2.1 配置数据源

    用户同步采用直接通过SQL的方式读取第三方的用户信息,泛微OA中的用户和机构信息存储在泛微OA的知识库中,可通过数据库视图的方式提取出如下两张表的信息,具体表结构如下:
用户表:oa_user

字段名称

说明

c_userid

用户ID(OA中用户ID,同步没有用到)

c_username

用户名(工号)

c_useralias

用户别名(姓名)

c_userpwd

密码(大写MD5加密串)

c_isenabled

是否有效(0/1)

c_defaultgroup

所属机构(OA中一个用户只能属于一个机构)

机构表:oa_group

字段名称

说明

c_groupid

机构名称(OA中的机构ID)

c_groupname

机构别名(OA中机构名称可能重复,所以不能以此为BI的机构名称)

c_groupdesc

机构描述(OA中没有机构描述,故字段没有同步)

c_pgroupid

所属机构名称(OA中的父机构ID)

c_isenabled

是否有效(0/1)

    由于数据存在于OA的库中,故需要配置一个数据源,用于连接OA的库。由于本次示例代码写死了同步需要使用的数据源ID为:DS.用户同步。所以需要在Smartbi系统中配置一个名称为:用户同步 的数据源,连接OA的库。示例配置如下图所示:
    
    配置好以后,如果要检验库中是否存在oa_user和oa_group两张表或数据库视图,则可通过基于创建的数据源创建原生SQL查询的方式,分别查询两种表,判断是否存在,并数据正常。

2.2.2 创建任务

    数据源配置好以后,就可以创建任务,同步用户了。具体任务配置如下图所示。
    

示例代码:

importPackage(Packages.java.io);
importPackage(Packages.java.lang);
importPackage(Packages.java.util);
importPackage(Packages.smartbi.usermanager);
importPackage(Packages.smartbi.sdk);
importPackage(Packages.smartbi.sdk.service.user);
importPackage(Packages.smartbi.sdk.service.datasource);
  
/**
 * 从外部数据库中定时同步用户、机构信息到知识库中
 */
var usrManagerService = new UserManagerService(connector);
var datasrcService = new DataSourceService(connector);
  
//
// 创建"顶级机构"
// 下面这段代码不能删除,如果不需要"顶级机构"可以将needTopGroup设置为false  
//
var needTopGroup = false; // 是否创建一个"顶级机构"
var defaultGroup = usrManagerService.getDepartmentById("DEPARTMENT");
  
//
// 同步机构:获取第三方系统中的机构信息
//
var sqlGroup = "SELECT c_groupid, c_groupname, c_pgroupid, c_isenabled FROM oa_group"; // 该SQL语句获取第三方系统中的机构信息
var gridDataGroup = datasrcService.executeNoCacheable("DS.用户同步", sqlGroup);
for (var i = 0; i < gridDataGroup.getRowsCount(); i++) {
    var orgName       = gridDataGroup.get(i, 0).getValue();
    var orgAlias      = gridDataGroup.get(i, 1).getValue();
    var orgParentName = gridDataGroup.get(i, 2).getValue();
    var isUse         = gridDataGroup.get(i, 3).getValue();
    if("".equals(orgName)){
    	continue;
    }
    // 判断机构是否已经同步
    var group = null;
    try {
        group = usrManagerService.getDepartmentByName(orgName);
    } catch(e) {}
    if (!group) { //如果不存在则创建
    	//判断机构是否可用,如果不可以,直接跳过。
    	if("0".equals(isUse)){
    		System.out.println(" 跳过组: "+orgName);
    		continue;
    	}
        var parentGroup = null;
        try {
            parentGroup = usrManagerService.getDepartmentByName(orgParentName);
        } catch(e) {}
        if (!parentGroup)
            parentGroup = defaultGroup;
        usrManagerService.createDepartment(parentGroup.getId(), orgName, orgAlias, "", "");
    }else{
    	//判断机构是否可用,否则删除
    	if("0".equals(isUse)){
    		System.out.println(" 删除组: "+orgName);
    		usrManagerService.deleteDepartment(group.getId());
    		continue;
    	}
    	if(!group.getAlias().equals(orgAlias)){
    		System.out.println(group.getAlias()+" 更新组别名: "+orgAlias);
    		usrManagerService.updateDepartment(group.getId(), orgAlias, "", "");
    	}
    }
}
//移组
System.out.println("移组移组》》》》》》》》》》》》");
for (var i = 0; i < gridDataGroup.getRowsCount(); i++) {
	var orgName       = gridDataGroup.get(i, 0).getValue();
    //var orgAlias      = gridDataGroup.get(i, 1).getValue();
    var orgParentName = gridDataGroup.get(i, 2).getValue();
    var isUse         = gridDataGroup.get(i, 3).getValue();
    if("0".equals(isUse)){
    	continue;
    }
	var parentGroup = null;
	var group = null;
	try {
	     group = usrManagerService.getDepartmentByName(orgName);
	    } catch(e) {}
	try {
	     parentGroup = usrManagerService.getDepartmentByName(orgParentName);
	} catch(e) {}
	//如果父组为空,则设置为根组。
	if(!parentGroup){
		System.out.println(orgName+" 父组赋值为默认组:"+orgParentName);
		parentGroup = defaultGroup;
	}
	if (group && parentGroup && (parentGroup.getId() != usrManagerService.getParentDepartment(group.getId()).getId())) {
		System.out.println(usrManagerService.getParentDepartment(group.getId()).getName()+" 移动组: "+orgParentName);
		usrManagerService.moveDepartment(group.getId(), parentGroup.getId());
	}
}
//
// 同步用户:获取第三方系统中的用户信息
//
var sqlUser = "SELECT c_username, c_useralias, c_userpwd, c_isenabled, c_defaultgrp FROM oa_user"; // 该SQL语句获取第三方系统中的用户信息
var gridDataUser = datasrcService.executeNoCacheable("DS.用户同步", sqlUser);
for (var i = 0; i < gridDataUser.getRowsCount(); i++) {
    var userName  = gridDataUser.get(i, 0).getValue();
    var userAlias = gridDataUser.get(i, 1).getValue();
    var userPwd   = gridDataUser.get(i, 2).getValue();
    var isUse     = gridDataUser.get(i, 3).getValue();
    var orgName   = gridDataUser.get(i, 4).getValue();
    if("".equals(userName)){
    	continue;
    }
    // 获取用户所属机构ID
    var parentGroup = null;
    var usrOrgIdList = [];
    parentGroup = usrManagerService.getDepartmentByName(orgName);
    //如果所属机构不存在,则设置默认组作为用户所属机构。
    if(!parentGroup){
    	parentGroup = defaultGroup;
    }
    usrOrgIdList.push(parentGroup.getId());
    // 判断用户是否已经同步
    var user = usrManagerService.getUserByName(userName);
    if (!user) {
    	if("0".equals(isUse)){
    		System.out.println("用户跳过:"+userName);
    		continue;
    	}
        usrManagerService.createUser(usrOrgIdList[0], userName, userAlias, "", "123456", true);
        user = usrManagerService.getUserByName(userName);
    }else{
    	if("0".equals(isUse)){
    		System.out.println("用户删除:"+userName);
    		usrManagerService.deleteUser(user.getId());
    		continue;
    	}
    	//更新用户的默认组,由于泛微OA一个用户只有一个组,故而直接使用默认组的方式
    	var defaultOrg = usrManagerService.getDefaultDepartment(user.getId()).getId();
    	//判断当前用户所属组是否与BI中的一致,否则进行更新。
    	if(!defaultOrg.equals(usrOrgIdList[0])){
    		System.out.println(usrOrgIdList[0]+" 更新组:"+defaultOrg);
    		usrManagerService.assignDepartmentsToUser(user.getId(), usrOrgIdList);
    	}
    }
    //第三方系统中用户密码是以大写的MD5加密的方式储存的,则需要在密码前加"0",方可正常登陆
    var encryptedPwd = "0" + userPwd.toLowerCase();
    var biPwd = usrManagerService.getPassword(user.getName());
    //如果当前用户别名或密码与BI中保存的不一致,则进行更新。
    if(user && (!user.getAlias().equals(userAlias) || !biPwd.equals(encryptedPwd))){
    	System.out.println(user.getAlias()+"更新用户别名:"+userAlias);
    	System.out.println(biPwd+"更新用户密码:"+encryptedPwd);
    	usrManagerService.updateUserByEncryptedPassword(user.getId(), userAlias, "", encryptedPwd, true);
    }
}

2.2.3 创建计划

    任务创建好以后,就可以配置一个执行计划。计划的配置可根据实际情况。示例配置如下图所示:
     

3 单点登录

3.1 登录逻辑

    采用隐式登录的方式,即在泛微OA的登录界面中添加Smartbi的登录逻辑;另外,修改泛微OA的注销逻辑中添加Smartbi的注销,在泛微OA点击注销登录的同时,也会注销Smartbi的登录。本次采用浏览器端SDK的方法,具体可参考使用浏览器端SDK单点登录到Smartbi
    当用户在泛微OA的登录界面中输入用户名和密码登录以后,再使用界面输入的用户名和密码去登录Smartbi系统。
    示例:
    (1)在泛微OA的登录页面中添加Smartbi的登录逻辑,下载示例JSP:login.jsp
Smartbi登录代码:

//登录SMARTBI
if(form1.loginid.value!="sysadmin"){
    var config = new Object();
    config.baseURL = "/smartbi/vision/";//smartbi服务器的URL地址
    var BOF_UI_DEBUG = false;
    // 创建全局唯一的JS装载器
    var jsloader = new JSLoader(config);
    // 创建应用程序对象
    var userService = jsloader.imports("bof.usermanager.UserService");
    // 通过userService.getInstance()可以调用所有的UserManagerModule方法.
    var result = userService.getInstance().login(form1.loginid.value, form1.userpassword.value);
    if (result) {
    } else {
        window.top.Dialog.alert("SMARTBI登录失败");
//         return false ;
    }
}


    (2)在泛微OA的注销页面中添加Smartbi的注销逻辑,下载示例JSP:toolbar.jsp
Smartbi注销代码:

function logout(){
	top.Dialog.confirm("<%=SystemEnv.getHtmlLabelName(16628,user.getLanguage())%>",function(){
		//退出SMARTBI
		 var config = new Object();
		    config.baseURL = "/smartbi/vision/";//smartbi服务器的URL地址
		    var BOF_UI_DEBUG = false;
		    // 创建全局唯一的JS装载器
		    var jsloader = new JSLoader(config);
		    // 创建应用程序对象
		    var userService = jsloader.imports("bof.usermanager.UserService");
		    // 通过userService.getInstance()可以调用所有的UserManagerModule方法.
		    var result = userService.getInstance().logout();
		window.location='/login/Logout.jsp';
	})
}

3.2 注意事项

3.2.1 Smartbi登录失效问题

    注意,这种登录方式实际上并没有打开Smartbi的页面,所以,如果长时间没有操作OA以后再去打开Smartbi系统或Smartbi报表,则可能BI的登录已经失效。为解决此问题,泛微OA可以在打开Smartbi系统的链接上再次添加登录BI的方法。下载示例页面:LoginSmartBI.jsp。将LoginSmartBI.jsp部署到泛微OA的login目录中,然后在泛微OA中配置Smartbi的链接。
    
示例代码:

<%@ page import="weaver.general.Util"%>
<%@ page import="weaver.hrm.User"%>
<%@ page import="weaver.hrm.HrmUserVarify"%>
<%@ page import="java.util.Map"%>
<script type="text/javascript" src="/js/smartbi/JSLoader.js"></script>
<%@ include file="/systeminfo/init_wev8.jsp" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%
String password = (String) session.getAttribute("password");
	if(!user.getLoginid().equals("sysadmin")){%>
		<script type="text/javascript">
		    var config = new Object();
		    config.baseURL = "/smartbi/vision/";//smartbi服务器的URL地址
		    var BOF_UI_DEBUG = false;
		    // 创建全局唯一的JS装载器
		    var jsloader = new JSLoader(config);
		    // 创建应用程序对象
		    var userService = jsloader.imports("bof.usermanager.UserService");
		    // 通过userService.getInstance()可以调用所有的UserManagerModule方法.
		    var result = userService.getInstance().login("<%=user.getLoginid()%>", "<%=password%>");
              location.href="/smartbi";
		</script>
<%}%>

    另外,可以配置BI登录的失效时间,如果失效时间设置大一些,则可更大限度地避免此问题。具体配置方法可参考Session失效时间设置:会话时间

3.2.2 跨域问题

    本次单点登录涉及跨域问题,通过在泛微OA中部署BI代理服务器的方法解决。相关内容可参考Smartbi与第三方系统集成跨域问题解决办法

    注意,实际部署的步骤与wiki描述稍有不同,在部署的时候,由于泛微OA的特殊配置,需要将classes文件夹从WEB-INF移动到smartbi目录下。

4 报表集成

    页面集成通过URL链接的方式,可以实现双向集成:泛微OA打开Smartbi资源,以及从BI打开泛微OA的资源。
      (1)泛微OA集成BI资源采用URL:/smartbi/vision/openresource.jsp?resid=...&paramsInfo=[{name:..., value=...}]

      (2)BI集成泛微OA采用URL:/workflow/request/ViewRequest.jsp?requestid=...

泛微OA的报表脚本示例代码:

	//主表根据参数查看报表
	jQuery(document).ready(function(){
   var lianjie = "field12017";//超链接字段名称,字段类型,多行文本
   var code = "field9393";//工号
   var code2 = "field9393";//工号2
			window.setInterval(setURL, 500);
			function setURL() {
				if(jQuery("#"+code).val()!=""){
					 jQuery("#" + lianjie + "span").html("<a href='/smartbi/vision/openresource.jsp?resid=I8a48819401581e361e3653070158334925294620&paramsInfo=[{name:\"user\",value:\""+jQuery("#"+code).val()+"\"},{name:\"user2\",value:\""+jQuery("#"+code2).val()+"\"}]' target='_blank'>报表查看</a>");
					}
			}	
			
			
});

//明细表根据参数查看报表
	jQuery(document).ready(function(){
   var lianjie = "field12021";//超链接字段名称,字段类型,多行文本
   var code = "field12020";//工号
			window.setInterval(setURL, 1000);
			function setURL() {
			var rowindex0_ = jQuery('#indexnum0').val() - 1;
			for (var num=0; num<= rowindex0_;num++) {
				if(jQuery("#"+lianjie+"_"+num).length>0&&jQuery("#"+code+"_"+num).val()!=""){
					 jQuery("#" + lianjie+"_"+num+ "span").html("<a href='/smartbi/vision/openresource.jsp?resid=I8a48819401581e361e3653070158334925294620&paramsInfo=[{name:\"user\",value:\""+jQuery("#"+code+"_"+num).val()+"\"}]' target='_blank'>报表查看</a>");
					}
			  }	
		
		 }
			
});

Smartbi报表脚本示例代码:

function main(simpleReport, simpleReportContext) {
    debugger;
    var col1 = simpleReport.getFieldIndexByName("requestId");
    var col2 = simpleReport.getFieldIndexByName("lcbh");
    var col3 = simpleReport.getFieldIndexByName("query");
    var rows = simpleReport.grid.getRowCount();
    var host = window.location.origin;
    if (host == "http://10.200.1.20:18080") {
        host = "http://oa.fanwei.com";
    }
    for (var i = simpleReport.grid.getHeaderRows(); i < rows; i++) {
        var cell1 = simpleReport.grid.getCell(i, col1);
        var cell2 = simpleReport.grid.getCell(i, col2);
        var cell3 = simpleReport.grid.getCell(i, col3);
        var htmlT = "<a href='" + host + "/workflow/request/ViewRequest.jsp?requestid=" + cell1.innerHTML + "' target='_blank'>" + cell2.innerHTML + "</a>";
        var htmlT2 = "<a href='" + host + "/workflow/request/ViewRequest.jsp?requestid=" + cell1.innerHTML + "' target='_blank'>查看流程</a>";
        cell2.innerHTML = htmlT;
        cell3.innerHTML = htmlT2;
    }
}

    

 

  • 无标签