最近公司有一个之前已经上线的项目,因为这个项目中有一些报表经常搞死tom猫的原因,现在要将报表的部分拆分出来成为一个独立的系统,在专门的一个猫上跑。
最终实现的一个效果如图所示:
1.非报表业务访问主系统走的是7088服务器
2.报表业务访问的是8082服务器上的信息
注意:报表业务链接后面携带了一个参数
-------------------------------------------------------------------------------------------------------------------------------
开始的一些准备:
1.报表系统是复制原系统一份,修改一下另一个系统的名字(不过用的只有报表的部分而已,别的没必要删除,太复杂了,牵扯到一些权限的问题,那些报表在那个角色下该不该显示等等)
2.准备两个一样的tomcat(这两猫要在同一个机器上启动,所以这时候要注意端口的冲突问题,里面有关port的都要改一下)
3.这两个项目不要放在同一个myeclipse或者eclipse中运行(原因:虽然可以正常启动访问,但是没法调试,两个除了名字不一样,其它都一样的项目在一个工作空间中的debug的时候,永远找的都是放在第一位的项目)
4.在每个工具里安装准备好的猫即可
5.复制相同的数据库,一个用于A系统,一个用于A_report系统
6.在A系统的数据库的权限表中加入一个开关,用来标记哪些是报表的URL,这个用来在页面遍历出的URL中进行判断(boolean ipflag)
--------------------------------------------------------------------------------------------------------------------------------------------------
开始具体的操作:(这里以A和A_report两个项目名为例)
考虑的问题:(1).两个系统要保证是同一个用户登陆,A_report系统何时触发登陆,怎么登陆 (2).在A_report系统中要去掉登陆超时的一些类似验证 。。。。
1.在A系统中添加一个properties属性文件,内容:reportIP=localhost:8082
2.当A系统的侧边栏目加载的时候,报表部分的URL变成A_report系统的对应栏目的地址,比如上面第二张图中所示
实现:(1)在加载A系统侧边栏目的action中入读取1中的IP属性文件,并声明一个变量,get,set 使其可以在前端页面中可以接收到,(其实是将需要的数据传递到前端,然后拼成相应的地址)
aciton类:
String fileName="reportIP.properties";
String reportIP = "";
try {
Properties p = new Properties();
//InputStream is=new FileInputStream(fileName);
InputStream in = PromissionAction.class.getClassLoader().getResourceAsStream(fileName);
p.load(in);
reportIP = p.getProperty("reportIP");
System.out.println(reportIP);
} catch (Exception e) {
e.printStackTrace();
}//获取当前登陆账户的唯一标识,(将此标识传递到第二个系统中,第二个系统根据此值实现登陆)
SysAccounts so = (SysAccounts) getRequest().getSession().getAttribute("SYSaccount");
String opercode = so.getOpercode();
getRequest().setAttribute("opercode", opercode);
前端页面判断:(用的是freemarker模版)
<#if p1.action?exists> <!--这里的p1就是权限表promission-->
<#if p1.ipflag==true>'http://${reportIP}${p1.action?if_exists}?opercode=${opercode?if_exists}'
<#else>'${p1.action?if_exists}'
</#if>
(2)至此,A系统的完成
3.A_report系统:
PS:在这个系统中有A系统的URL访问的时候,实现登陆,而且只能登陆一次,每次都登陆有影响
(1)首先得在这个系统中定义一个拦截器,将此拦截器配置到这个系统的action 的xml文件中,然后每个报表的action都需要ref一下,这样一来,不管在A系统中先点那个报表的URL,都会实现第二个系统的登陆操作
public class ReportPamamInterceptor implements Interceptor {
private SysAccounts accounts;
public void destroy() {}
public void init() {}
public String intercept(ActionInvocation invocation) throws Exception {
//从系统session中获取登陆用户信息
HttpServletRequest req = ServletActionContext.getRequest();
SysAccounts accounts = (SysAccounts) req.getSession().getAttribute("SYSaccount");
//以下3句是得知那个action访问的,从而判断返回哪个报表
String namespace = invocation.getProxy().getNamespace();
String method = invocation.getProxy().getActionName();
String url = namespace + "/" + method + ".do";
//判断用户如果不为空,就不再登陆,直接返回具体的相应页面,在report.xml中的 <global-results>进行接收
if (accounts != null) {
return JudgeResult.toGoal(url);
}
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
String opercode = request.getParameter("opercode");
accounts = new SysAccounts();
accounts.setOpercode(opercode);
session.setAttribute("accounts", accounts);
//用于在登陆页面进行判断跳转的报表
session.setAttribute("url", url);
return "goLogin";
}
}
xml文件中配置:
<package name="querycardinfo" namespace="/unionfb/report/querycardinfo" extends="default">
<!-- 新增拦截器,作用是 当第一个系统访问此action的时候触发拦截器,拦截器接收第一个系统传递的值 -->
<interceptors>
<interceptor name="reportPamamInterceptor"
class="com.zframework.system.interceptor.ReportPamamInterceptor">
</interceptor>
<!-- 拦截器栈 -->
<interceptor-stack name="mydefault">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="reportPamamInterceptor" />
</interceptor-stack>
</interceptors>
<!-- 定义全局的,拦截器返回之后跳转到本系统进行登陆操作 -->
<global-results>
<!-- 没有登陆的时候执行-->
<result name="goLogin" type="redirect">/Login.do</result>
<!-- 已经登陆跳转 拦截器中检测到用户已经登陆,就不再做登陆操作,直接在这里返回具体的相应页面 -->
<result name="/unionfb/report/querycardinfo/cro.do" type="redirect">/unionfb/report/querycardinfo/cro1.do</result>
</global-results>
<!-- 第二个系统对第一个系统的接应点 -->
<action name="cro" class=""> //这里的class属性不能丢,这个action只是一个接应点,触发拦截器的作用是拦截器触发之后判断第二个系统是否已经登陆,
<interceptor-ref name="reportPamamInterceptor"/>
</action>
<action name="cro1" class="queryCardAction" method="cro" >
<result name="success">/templates/unionfb/report/card/query.html</result>
<result name="error" type="dispatcher">/templates/common/message.html</result>
(2)定义一个普通的Java类进行判断
public class JudgeResult {
// 注意:以下的路径不是地址,仅是一个标记而已
public static String toGoal(String url) {
switch (url) {
// 网店交易汇总表
case "/report/wdsellcardquery.do":
return "/report/wdsellcardquery.do";
......
JudgeResult.toGoal(url)判断直接跳到<global-results>的result返回
JudgeResult.toGoal(url)结果
------------------------------------------------------------------------over-----------------------------------------------------------------------------------
这种做法比较low,如果大家有比较好的想法,可以交流下。
PS:刚刚做完发现一个bug,这是两个系统,也就是说会产生两个session,后一个系统生成的session会把前一个系统的session覆盖掉,所以A_report还要放在不同的服务器上
PS:数据库的主从库之间要同步,报表里的数据是来自于非报表录入的了,此时的从库只能设置为只读的,这样的话,A_report系统中会发生一个数据库错误:ORA-16000(Oracle数据库),出现此错误的原因:系统的一些查询语句有添加的操作,导致这样的错误发生