办公自动化系统学习笔记.doc_第1页
办公自动化系统学习笔记.doc_第2页
办公自动化系统学习笔记.doc_第3页
办公自动化系统学习笔记.doc_第4页
办公自动化系统学习笔记.doc_第5页
已阅读5页,还剩39页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

开发OA项目的目的熟悉OA系统的核心需求体会项目开发流程掌握当前流行开发框架的集成开发方法和技巧掌握需求分析方法掌握授权与认证系统以及公文流转的设计模式授权与认证系统是每个项目的基础(不一定是核心)授权与认证系统不是一个简单的登录系统熟悉利用struts+spring+hibernate架构的开发过程熟悉jsp的开发熟悉javascript的开发(Ajax)总体概述需求分析* 需求的确定,了解用户方的决策者,搞好关系,快速确定整体需求(参与者,做什么等的整体方向)* 需求的访谈,了解系统参与者的具体工作,工作流程,是否使用表单等详细信息。必须是正确的必须是可行的必须是对项目来说必不可少的必须是被标明优先次序的必须是不含糊的必须是能够被证实的(能够测试)系统分析与设计架构分析与设计业务逻辑分析与设计界面设计开发环境搭建开发测试开发测试文档编纂OA系统需求分析功能概述企业门户首页组织管理机构管理人员管理权限管理角色管理模块(功能)管理用户管理授权管理公文管理(公文流转)添加,删除,修改,上传公文待审批公文公文流程定制信息交流发件箱收件箱信息发布信息类别管理(新闻,通知等)信息内容管理(发布)个人事务我的任务(提醒)通信录我的便签知识管理项目管理资产管理考勤管理人事档案职能型的组织机构用面向对象建模的方法开发项目抽象出相关的领域模型用面向对象的思维从领域模型中抽象出相关的对象.根据对象创建相关的实体类.为实体类创建映射文件根据实体类创建数据库表.分模块,自底向上开发领域模型,持久化层确定实体类确定相关映射文件业务逻辑层根据相关概念和关联创建业务逻辑接口实现业务逻辑接口呈现层jstl+struts+el职能型组织机构设计开始:使用ant+xdoclet设计构建任务文件,build.xml,自动生成hibernate.cfg.xml和映射文件.使用hibernate.cfg.xml文件中的hbm2ddlauto属性配置为update,使得服务器启动的时候可以自动的创建数据库表.创建实体对象Person, OrganizationPerson中持有Organization的一个引用,是一个many-to-one的关系类中使用xdoclet的javadoc注释方式/* hibernate.many-to-one column=”organization”*/private Organization organization;配置文件中自己配置的many-to-oneOrganization中是一个树形结构(双向关联).类中使用xdoclet的javadoc注释方式/* hibernate.many-to-one column=”organization”*/private Organization parent;/* hibernate.set* hibernate.key column=”pid”* hibernate.one-to-many class=”selfImpr.oa.model.Organization”*/private Organization child;配置文件中自己的配置编写单元测试类,测试many-to-one和one-to-manymany-to-one parent 形成的树形结构one-to-many set child形成的树形结构做单元测试的时候可以把可能产生影响的代码先注掉.Hibernate映射: many-to-one是在自身产生一个外键关联字段, one-to-many 是在对方产生一个外键关联字段.在one-to-many的映射中的set中设置inverse=”true”, 可以限制只能从多的一方持有一的一方的引用.设置了inverse=”true”后,如果由一的一方持有多的一方的引用进行存储,pid不会被存入. inverse强制使得关联必须由many的一方进行维护. 保证了对一的一方进行维护时,可能把一的一方所持有的多的一方的引用置空.对于inverse的设置,在使用xdoclet时,可以在类中的一对多引用属性上加上hibernate.set inverse=”true”Dao是为了实现持久化策略的可插拔设计.如果持久化策略需要变更,就要设计Dao.否则,就不需要设计Dao.业务逻辑层分人员管理和机构管理,画出用例图根据用例图设计接口使用spring为OrgManagerImpl添加声明式事务,并且由OrgManagerImpl继承HibernateDaoSupport类,使用hibernate封装了的session(HibernateTemplate), 使用HibernateTemplate就需要为OrgManagerImpl注入sessionFactory属性.使用HQL语言的时候,是对对象的操作, 所以,可以使用类 对象, 然后调用属性的方式进行查询.例如:“FROM Organization o WHERE o.parent.id = ? “使用OpenSessionInViewFilter的时候,必须在hibernate.cfg.xml中配置thread标识currentSession就是当前线程的sessionFilter是跨越整个请求过程的. 浏览器发出一个请求,在到达action之前就会到达Filter先进行处理,然后,在action经过一系列处理之后,response返回的过程中会再次到达Filter,这样就可以利用Filter实现OpenSessionInView,第一次到达openSession, 第二次到达closeSession.Filter(继承javax.servlet.Filter)中间进行处理的时候,是调用了doFilter()方法,第一次到达首先执行chain.doFilter();之前的内容,第二次到达,执行chain.doFilter()之后的内容OpenSessionInView的根源在于Lazy加载机制.如果没有在web.xml中配置和也可以通过BeanFactory factory = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext();来获取BeanFactory;JTA是适用于多个数据库的事务的api表现层注入struts的action的时候,需要在struts-config.xml文件中使得action的type=”org.springframework.web.struts.DelegatingActionProxy”, 并且在applicationContext-actions.xml文件中注入的过程中需要设定scope: 配置了scope=prototype可以防止action的单例带来的线程安全问题.通过struts的自动异常处理机制进行异常的统一处理. 对于单个Action中的异常统一处理, 通过在当前action标签内设置指定exception标签进行处理手工异常处理(编程式异常): ActionMessages msgs = new ActionMessages();ActionMessage msg = new ActionMessage(String name);msgs.add(String name, msg);this.saveErrors(msgs);return mapping.findForward(“exception”);用key指定国际化消息文本中的key值, 用type指定exception的类型. 用scope指定异常消息放到哪个scope中, path用以指定异常处理页面. 如果没有指定path, 就寻找action中定义的input属性指向的页面.可以使用global-exception处理所有的没有指定局部exception的action中的对应exception自动异常处理机制的缺点是没有办法为国际化消息文本中加入占位符的参数.可以通过自定义ExceptionHandler处理异常struts-config.xml中可以配置通过指定handler使用自定义的handler处理exception在自定义的Exception中重新构建自己的构造器,通过加入key值(可以是多个)进行判断具体的处理方式.(如果需要创建动态国际化消息文本, 就需要加入动态参数机制)自定义的ExceptionHandler中需要遵循如下编写规则:继承org.apache.struts.action.ExceptionHandler类重写execute()方法.方法中判断如果是异常,如果是自己要处理的目标异常类型,就处理, 不是的话把异常抛给父类处理(return super.execute(.)通过强制转型, 得到目标exception, 拿到自定义的key, 然后chuangian国际化消息文本, 如果是多个key的情况, 可以通过获取定义的多个参数的Object数组的是否为空进行判断并创建对应的国际化消息文本通过ExceptionConfig对象的getPath()方法可以拿到配置文件中关于这个异常的配置ActionMessage 可以通过判断是否有key进行创建,如果目标Exception是通过指定的key创建的, new ActionMessage(ae.getKey(); 没有指定的情况下通过new ActionMessage(ExceptionConfig.getKey(); 直接使用配置文件中的key创建国际化消息文本.用于重定向的ActionForward对象则可以根据是否配置有path进行处理, 对于没有配置path的情况直接使用mapping.getInputForward()进行专项, 对于配置了path的情况则使用指定path创建Forward对象.最后需要调用父类(org.apache.struts.action.ExceptionHandler)封装的storeExcetpion(HttpServletRequest request, String property, ActionMessage error, ActionForward forward, String scope)方法.在storeException()中首先会根据property和error创建一个ActionMessages对象,然后判断scope,如果scope是request就把error信息存放到requestScope,如果不是就存放到sessionScope中.最终返回生成的Forward对象.由于只进行自动异常处理会导致异常信息丢失, 不方便调试, 所以,需要通过日志处理的方式把异常信息记录下来, 由开发人员根据异常信息对程序进行调试.jdk 1.4 以上的版本支持日志处理, java.util.logging 包是jdk支持的日志处理mons log 是通用的log处理commons log是一个抽象接口, 底层的实现可以自动替换:如果当前存在log4j, 则使用log4j来实现.否则, 使用jdk logging api来实现.如果不存在以上两种, 才使用自身的简单实现.commons log是通过LogFactory.getLog(Class c)来获取一个logger对象进行log处理的Log logger = LogFactory.getLog(Test.class);Log4j: 比jdk logging更加成熟, 是事实上的日志记录标准Log4j的三大概念: logger 使用这个对象进行输出.appender 使用这个对象定义输出到哪里.layout 使用这个对象定义输出的格式.Log4j的级别(DEBUG INFO WARN ERROR FATAL): 级别越小, 输出信息越全面. 定义级别后输出该级别以上所有级别的信息.一般而言, 开发的时候使用debug级别, 部署后的级别应该设置为warn级别.logger的级别, appender, layout都是可以继承的. 局部优先级高于全局.Log4j的配置示例:控制台输出的appender和layout的配置.log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%dABSOLUTE %5p %c1:%L - %m%n输出到文件的appender和layout的配置(log4j.appender.file.File指定的文件路径如果使用相对路径, 当前路径是项目的根路径.)log4j.appender.file=org.apache.log4j.FileAppenderlog4j.appender.file.File=WebRoot/log/oa.loglog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=%dABSOLUTE %5p %c1:%L - %m%n定义根logger(级别, appender)log4j.rootLogger=debug, stdout,fileLog4j的基本配置规则Logger的配置log4j.rootLogger=级别, 使用的appender列表log4j.logger.logger的名称=级别,使用的appender列表Appender的配置log4j.appender.appender的名称=appender类名log4j.appender.appender的名称.appender的属性名=appender的属性值对于ConsoleAppender来说, 可以配置Target属性为System.out对于FileAppender来说, 可以配置File属性为目标输出文件.Layout的配置log4j.appender.apender的名称.layout=layout类名log4j.appender.appender的名称.layout.layout的属性名=layout的属性值最常见的Layout是PatternLayout(Log4j采用类似C语言中的printf函数的打印格式控制日志信息的格式)%m 输出代码中的指定消息%p 输出优先级, 即DEBUG, INFO, WARN, ERROR, FATAL%r 输出自应用启动到输出该log信息耗费的毫秒数%c 输出所属的类目, 通常就是所在类的全名%t 输出产生该日志时间的线程名%n 输出一个回车换行符, windows平台为”rn”, unix为”n”%d 输出日志时间点的日期或时间, 默认格式为ISO8601, 也可以在其后指定格式, 比如%dyyyy MM dd HH:mm:ss, SSS%1 输出日志时间的发生位置, 包括类目名, 发生的线程, 以及在代码中的行数.自定义Layout模板log4j.appender.file.layout.ConversionPattern=Time: %dyyyy-MM-dd HH:mm:ss,SSS | Level: %5p | Thread: %t | ClassPath: %c:%L | Message: %m%n使用pager-taglib进行分页显示:pager-taglib的使用步骤.拷贝pager-taglib.jar包在jsp页面中使用taglib指令引入pager-taglib标签库.使用pager-taglib标签库进行分页处理pager-taglib的具体使用: 首页 前页 $pageNumber 后页 尾页pager-taglib的使用技巧:通过jstl判断,为指定页码显示特定效果:$currentPageNumber $pageNumber 通过指定url的参数.分页解决方案:将Manager使用一个AbstractManager抽象类进行封装:public abstract class AbstractManager extends HibernateDaoSupport public PageModel searchPaginated(String hql, Object params) Query countQuery = getSession().createQuery(getCountHql(hql); Query dataQuery = getSession().createQuery(hql); if(params != null & params.length 0) for(int i=0; iparams.length; i+) countQuery.setParams(i, paramsi); dataQuery.setParams(i, paramsi); int total =( (Long)countQuery.uniqueResult().intValue(); List datas = dataQuery.list(); PageModel pm = new PageModel(); pm.setTotal(total); pm.setDatas(datas); return pm; private String getCountHql(String hql) int index = hql.indexOf(“FROM”); if(index != -1) return “SELECT COUNT(*) “ + hql.substring(index); throw new SystemException(“无效的HQL查询语句!”); 采用ThreadLocal为每个用户访问线程提供单独的分页参数(offset, pagesize)机制:public class SystemContext private static ThreadLocal offset = new ThreadLocal(); private static ThreadLocal pagesize = new ThreadLocal(); public int getOffset() Integer value = offset.get(); if(value = null) return 0; return value; public void setOffset(int offsetvalue) offset.set(offsetvalue); public void removeOffset() offset.remove(); public int getPagesize() Integer value = pagesize.get(); if(value = null) return 0; return value; public void setPagesize(int pagesizevalue) pagesize.set(pagesizevalue); public void removePagesize() pagesize.remove(); 采用统一的Filter进行分页参数值的获取public class PagerFilter implements Filter public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException HttpServletRequest httpRequest = (HttpServletRequest)request; SystemContext.setOffset(getOffset(httpRequest); SystemContext.setPagesize(getPagesize(httpRequest); try doFilter(request, response); finally SystemContext.removeOffset(); SystemContext.removePagesize(); private int getOffset(HttpServletRequest request) int offset = 0; String offsetStr = request.getParameter(“pager.offset”); if(offsetStr != null & !offsetStr.trim().equals(“”) offset = Integer.parseInt(offsetStr); return offset; private int getPagesize(HttpServletRequest request) return 10; 权限管理模块的开发用户可以拥有多个角色,角色可以被分配给多个用户.权限的意思就是对某个资源的某个操作.所谓资源,就是系统的模块.所谓操作,包括:增加,删除,修改,查询等操作.权限就是对”who, do what, how do”这三个方面的扩展.who 用户,角色do what 资源how do 操作.针对用户和资源之间关联的一种扩展, 加上一个访问控制规则权限就是对资源的CRUD的操作.RBAC(role base access controll): 基于角色的访问控制RBAC 0: 核心的rbac, 把权限分配给角色. 对用户和角色之间的关系进行了扩展, 抽象出会话的概念,用户在一次会话过程中,只能扮演(激活)一部分角色.RBAC 1: 在RBAC0基础上对角色进行了扩展,角色之间可以有继承和级别的关系.拥有上级角色,就拥有了下级角色的全部权限.RBAC 2: 在RBAC0基础上对用户和角色之间的授权进行了约束限制,具体是对同一用户不能拥有冲突角色,时间,地点等方面的约束.RBAC 3: 对RBAC 1 和RBAC 2的一个整合.OA使用的权限控制:角色有优先级.使用访问控制列表(ACL)进行权限控制ACL中的对象主体类型: 表明主体是用户还是角色主体标识: 主题的id资源标识: 需要控制访问的资源id授权状态: int类型(32位), 用后4位表示CRUD操作.授权状态掩码:int类型(32位). -1(全1) 表示可以继承, 0 (全0)表示不可以继承用一个ACL对象表示某个主体,某个资源的多个操作授权状态的改变:public void setPermission(int permission, boolean yes) int temp = 1;temp = temp permission; /移位到需要修改的权限占位.if(yes) aclState |= temp; /如果赋予该权限,或运算,保持其他位不变,修改需要控制的位为真. else aclState &= temp; /如果取消该权限,与运算,保持其他位不变,修改需要控制的位为假. 获取授权:int temp = 1;temp = temp permission;temp &= aclState; /屏蔽其他位,拿到需要的位的权限值.实体类的创建资源类(Module):id: name: 模块名称sn: 模块唯一编号url: 模块的入口地址orderNo: 模块的显示顺序parent: 上级模块children: 下级模块角色类(Role):id:name: 角色名人员类(Person):id:name: 姓名organization: 所属机构user: 对应的用户信息用户类(User):id:username: 登录帐号password: 登录密码createTime: 创建时间expireTime: 失效时间person: 对应的人员信息用户角色类(UsersRoles): 将用户和角色之间的多对多关联转化成为两个一对多的关联id:user: 对应的用户role: 对应的角色orderNo: 角色在用户中的优先级访问控制类(ACL): id:principalType: 主体类型principalSn: 主体标识resourceSn: 资源标识aclState: 授权状态, 用int型的后四位表示CRUD操作是否允许aclTriState: 授权掩码, 表示授权状态是否继承,用0(全0)表示不继承, 用(-1全1)表示继承.一对一主键关联映射的实现(采用many-to-one的方式):class A /* * hibernate.many-to-one * unique=”true” /* B b;class B /* * hibernate.one-to-one * property-ref=”b” /* A a;用Calendar进行时间的操作Calender now = Calendar.getInstance();Calender expireTime = Calendar.getInstance();expireTime.setTime(user.getExpireTime();if(now.after(expireTime) throw new SystemException(“帐号已失效.”, “exception.user.login.expire”);Converter的使用:1. Converter类的实现 public class DateConverter implements mons.beanutils.Converter private static SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”); public Object convert(Class type, Object value) if(value = null) return value; if(value instanceof Date) return value; if(value instanceof String) try return format.parse(String)value); catch(ParseException e) e.printStackTrace(); .2. Converter的注册servlet:public class DateConverterInit extends HttpServlet public void init() throws ServletException mons.beanutils.ConvertUtils.register(new DateConverter(), Date.class); 可以通过在hibernate的映射文件中设置属性的update属性为false,让该属性在更新的时候不被改变.为用户分配角色:点击分配角色列出当前用户已有角色列表点击为当前用户分配角色列出所有角色列表选择角色设置优先级添加角色到用户.授权认证管理类:package selfImpr.oa.manager.impl;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import org.hibernate.Query;import selfImpr.oa.manager.AclManager;import selfImpr.oa.manager.SystemException;import selfImpr.oa.manager.UserManager;import selfImpr.oa.model.ACL;import selfImpr.oa.model.Module;import selfImpr.oa.model.Permission;import selfImpr.oa.model.Role;/* * 访问控制列表管理类 * author Administrator * */public class AclManagerImpl extends AbstractManager implements AclManager private UserManager userManager;public void setUserManager(UserManager userManager) this.userManager = userManager;/* * 设置或更新授权 * param principalType 主体类型 * param principalSn 主体标识 * param resourceSn 资源标识 * param permission 操作标识 * param yes 是否允许该操作 */public void addOrUpdatePermission(String principalType, int principalSn,int resourceSn, int permission, boolean yes) /根据主体类型,主体标识,资源标识获取一个ACL实例ACL confirm = getACL(principalType, principalSn, resourceSn);/表明是否获取到了ACL实例对象boolean flag = confirm = null;/如果没有获取到ACL实例,创建一个新的ACL实例,如果获取到了ACL实例,则acl指向获取到的ACL实例ACL acl = flag ? new ACL() : confirm;if (flag) /如果ACL实例是新创建的,设置其标识信息acl.setPrincipalType(principalType);acl.setPrincipalSn(principalSn);acl.setResourceSn(resourceSn);/设置ACL实例的授权信息acl.setPermission(permission, yes);getHibernateTemplate().saveOrUpdate(acl);/* * param userId 用户标识 * param resourceSn 资源标识 * param yes 是否继承 */public void addOrUpdateUserExtends(int userId, int resourceSn, boolean yes) /根据主体类型,主体标识,资源标识获取一个ACL实例ACL confirm = getACL(ACL.TYPE_USER, userId, resourceSn);/表明是否获取到了ACL实例对象boolean flag = confirm = null;/如果没有获取到ACL实例,创建一个新的ACL实例,如果获取到了ACL实例,则acl指向获取到的ACL实例ACL acl = flag ? new ACL() : confirm;if (flag) /如果ACL实例是新创建的,设置其标识信息acl.setPrincipalType(ACL.TYPE_USER);acl.setPrincipalSn(userId);acl.setResourceSn(resourceSn);/设置ACL实例的继承特性acl.setExtends(yes);getHibernateTemplate().saveOrUpdate(acl);public void delPermission(String principalType, int principalSn,int resourceSn) ACL acl = getACL(principalType, principalSn, resourceSn);if(acl = null) throw new SystemException(申请删除的授权信息不存在., exception.acl.del);getHibernateTemplate().delete(acl);/* * param userId 用户标识 * param resourceSn 资源标识 * param permission 操作标识 */public boolean hasPermission(int userId, int resourceSn, int permission) /初始一个授权状态,设定其授权状态为未知.int result = ACL.ACL_NEUTRAL;/根据主体类型,主体标识,资源标识获取一个ACL实例ACL userAcl = getACL(ACL.TYPE_USER, userId, resourceSn);/如果获取到ACL实例并且ACL实例表明该用户对该资源的授权是不继承的,/就根据当前ACL实例确定是否对该操作有授权if(userAcl != null & !userAcl.isExtends() result = userAcl.getPermission(permission);/允许操作返回true,不允许操作返回falsereturn result = ACL.ACL_ALOW ? true : false;/没有对该用户直接授权,或者该用户对该资源的授权是继承的,则根据用户的角色设置授权状态.result = getRolesPermission(userId, resourceSn, permission);/允许操作返回true,不允许操作返回falsereturn result = ACL.ACL_ALOW ? true : false;private int getRolesPermission(int userId, int resourceSn, int permission) /根据优先级从高到低获取用户的角色列表List roles = getRolesDesc(userId);/遍历角色列表for(Role role : roles) ACL acl = getRoleACL(role.getId(), resourceSn);if(acl != null) /如果某个角色对该资源有授权信息,返回该授权状态return acl.getPermission(permission);

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论