我的平台功能介绍

主体架构

主体采用Struts2 Spring3 Hibernate3.3架构。

 

Spring和Hibernate都采用Annotation方式省去了大量Bean的配置和ORM映射文件。

界面展示用JSP结合 Struts2 tags的方式。

 

主要模块的页面采用form,和list两个页面,前者负责新增、编辑、查看,后者负责数据列表展现。

 

权限管理

平台采用基于角色的访问控制RBAC模型,通过给角色授予访问某资源(模块,URL),再将角色分配给用户,使用户获得访问某些资源的权限。

 

对应的领域模型有“用户”User,“角色”Role,“模块”Module,“部门”Department。

 

权限采用Spring Security。

邮件发送组件

系统已在com.epro.crm.util.message.EmailHelper工具类中集成了JavaMail, 并在EmailService内置在了CommonBaseService中。如果业务需要发送邮件,只需在将邮件内容写在EmailServiceImpl中,然后在Service中直接调用即可。系统将异步调用EmailHelper.sendEmail(…)方法,将邮件发出。

 

参考:com.epro.crm.service.system.impl.UserServiceImpl.add(User)

注册用户时,系统将注册成功信息,异步的发送至用户的注册邮箱中。

protected void sendEmail(final List<String> recipientAccounts,final String message, final String subject){
		//判断 系统设置 (是否已开启 发送邮件模块)
		if(Constants.YES.equals(Configurations.SEND_EMAIL)){
			//异步发送
			taskExecutor.execute(new Runnable() {
				public void run() {
					EmailHelper.sendEmail(recipientAccounts,message, subject);
				}
			});
		} else {
			log.info("系统邮件发送功能未开启,无法发送短信");
		}
	}

界面表单验证

使用了jQuery validate 表单验证框架

参考 /page/user/user_form.jsp

jQuery(function(){
	$("#submitForm").validate({
		rules: {
			name: "required",
			username: {
				required: true,
				minlength: 4
				//remote:"UserAction!validateUsername"
			},
			phone:"digits",
			birthDay: "dateISO",
			email: {
				required: true,
				email: true
			}
		},
		success: function(label) {
            label.text('').addClass("success");
        },
		messages: {
			name: "请输入用户的姓名",
			phone:"电话号码必须是数字",
			username: {
				required: "请输入该用户的登录名(登录账号)",
				minlength: "登录名最短4位"
			},
			birthDay: "请输入合法日期格式 如:2011-3-11",
			email: "请输入合法email格式"
		}
	});
	
});

增删改查封装

 

普通的数据访问对象(Dao)接口应继承于com.epro.crm.dao.base.CommonDaoInterface<T>,其默认已有增删改查的接口。其实现类应继承于com.epro.crm.dao.base.CommonBaseDao<T>,默认已有增删改查等基本操作的实现,并可通过HibernateTemplate扩展其他的操作。如果需要按自定义的条件查询,可以重写CommonBaseDao类的String appendConditionHQL(T t)方法。

                    

参考:com.epro.crm.dao.system.impl.UserDaoImpl

 

增加

public void add(T t) {
		String stateStr = null;
		try {
			if (t instanceof DeletedByLogic ) {
				stateStr = BeanUtils.getProperty(t, "state");
				if(stateStr == null){
					BeanUtils.setProperty(t, "state",Constants.STATE_VALID);
				}
				hibernateTemplate.save(t);
			} else if (t instanceof DeletedByPhysics) {
				hibernateTemplate.save(t);
			} else {
				hibernateTemplate.save(t);
			}
		} catch (Exception e) {
			log.error(e.getMessage(),e);
			throw new CrmSystemException("新增数据出错",e);
		} 
	}
删除
public void delete(T t) {
		try {
			String idStr = BeanUtils.getProperty(t, "id");
			long id = Long.parseLong(idStr);
			Object delObject = hibernateTemplate.get(t.getClass(), id);
			if (t instanceof DeletedByLogic) {
				BeanUtils.setProperty(delObject, "state",
						Constants.STATE_DELETED);
			} else if (t instanceof DeletedByPhysics) {
				hibernateTemplate.delete(delObject);
			} else {
				throw new CrmSystemException("实体类 " + t.getClass().getName()
						+ " 既不是物理删除,也不是逻辑删除");
			}
		} catch (Exception e) {
			log.error(e.getMessage(),e);
			throw new CrmSystemException("删除对象" + t + "时,系统异常");
			
		}
	}

 查询

	public List<T> getList(T t, final int offset, final int length) {
		final String hql = getListHQL(t);
		List<T> list = getHibernateTemplate().executeFind(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
						Query query = session.createQuery(hql);
						query.setFirstResult(offset);
						query.setMaxResults(length);
						List<T> list = query.list();
						return list;
					}

					
				});
		return list;
	}
	
	
	
	/**
	 * 子类需要重写HQL时,可继承重写该方法
	 * @param t
	 * @return
	 */
	protected String getListHQL(T t){
		String className = t.getClass().getSimpleName();
		StringBuffer hqlBuffer = new StringBuffer("from " + className + " as t " + (appendJoinHQL(t)==null?"":appendJoinHQL(t)) + " where 1 = 1 ");
		String appender = appendConditionHQL(t);
		if(appender != null){
			hqlBuffer.append(appender);
		}
		if(t instanceof DeletedByLogic){
			hqlBuffer.append(" and t.state != '" + Constants.STATE_DELETED + "'");
		}
		hqlBuffer.append(" order by " + getOrderByHQL()+ " " );
		String hql = hqlBuffer.toString();
		return hql;
	}


统计条数
public int getCount(T t, int offset, int length) {
		String className = t.getClass().getSimpleName();
		StringBuffer hqlBuffer = new StringBuffer("select count(*) from " + className + " as t where 1=1 ");
		String appender = appendConditionHQL(t);
		if(appender != null){
			hqlBuffer.append(appender);
		}
		if(t instanceof DeletedByLogic){
			hqlBuffer.append(" and t.state != '" + Constants.STATE_DELETED + "'");
		}
		final String hql = hqlBuffer.toString();
		Object uniqueResult = createQuery(hql).uniqueResult();
		return Integer.parseInt(uniqueResult.toString());
	
	}


普通的业务处理对象(Service)接口应继承于com.epro.crm.service.base.CommonServiceInterface<T>,其默认已有增删改查的接口。其实现类应继承于com.epro.crm.service.base.CommonBaseService<T>,该类有增删改查,日志等功能。

 参考:com.epro.crm.service.system.impl.UserServiceImpl

public PageModel<T> getPageModel(T sample, PageModel<T> pageModel){
		if(sample == null)
			sample = getNewEntity();
		List<T> dataList = getCommonDao().getList(sample, pageModel.getOffset(), pageModel.getPageSize());
		Integer total =  getCommonDao().getCount(sample, pageModel.getOffset(), pageModel.getPageSize());
		pageModel.setDataList(dataList);
		pageModel.setTotalItemNumber(total);
		return pageModel;
	}

分页模型

页面上采用pager-taglib 标签进行分页页码显示及偏移量计算,由分页模型对象com.epro.crm.model.util.PageModel<T>接收偏移量offset,页大小,在DAO层由com.epro.crm.dao.base.CommonBaseDao.getList(T,int, int)方法将分页查询的结果放在PageModel对象中,在页面通过Struts-tag显示。

public class PageModel<T> {
	/** 
     * 总记录数 
     */  
    private Integer totalItemNumber;  
    /** 
     * 当前页结果集 
     */  
    private List<T> dataList;
    
    /** 
     * 偏移量:当前页第一条记录,在总记录数中的序号
     */
    private Integer offset = 0; 
    /**
     * 每页显示记录数
     */
    private Integer pageSize = Configurations.DEFAULT_PAGE_SIZE;
    /**
     * 页码
     */
    private Integer pageNumber = null;
    /**
     * 总页码
     */
    private Integer totalPageNumber = null;

    /** getters and setters **/
} 


数据字典

数据字典由“数据字典类型”com.epro.crm.model.system.DataDictType以及“数据字典项”com.epro.crm.model.system.DataDictItem组成。

 

在数据库中添加了数据字典类型,及对应的数据字典项后。在Action层中,通过DataDictTypeService准备数据字典内容。在界面中可通过

<s:select list="allTaskTypes" name="taskType.id" headerKey="" headerValue="请选择.." listKey="id" listValue="name" value="taskType.id" cssClass="required"></s:select>

的Struts标签,即可显示数据字典列表。


系统日志/操作日志    

平台提供两种日志方式。

 

系统日志:系统日志由log4j记录,输出至/crm_log.html文件中。程序中可通过以下方式获得日志记录对象。

 

protected final Log log = LogFactory.getLog(getClass());


 

操作日志:操作日志是由平台提供用来将用户的重要操作记录到数据库中,以及日志的显示模块。记录的内容包括操作时间,操作者用户名,IP,以及操作描述。见com.epro.crm.model.system.Log

 

已经在BaseAction中封装,所以可以在Action中直接使用logService.log(“”)记录。

 

参考:

 

public void log(String summary, String description){
       Log log = new Log((User)sessionMap.get(Constants.SESSION_USER),getIpAddr(),
              summary, description);
       logService.add(log);
    }

平台中的领域对象删除后的状态支持两种模式。

 

逻辑删除是采用一个标记字段表示该记录的状态,如已删除,则修改标记字段为删除状态,查询时将其忽略。物理删除即为普通delete删除。

 

采用物理删除的类中实现com.epro.crm.model.base.DeletedByPhysics接口即可。

采用逻辑删除的类中实现com.epro.crm.model.base.DeletedByLogic接口即可。

 

CommonBaseDao在删除和查询对象时,会根据类所表明的模式,进行判断。

 

参考:com.epro.crm.model.system.User

 


上传下载  

Struts 2 的常用方式,有待封装

见 ResumeAction:111

Json支持

由于使用Struts2 Json plugin 时,response默认content type为”text/json”,部分浏览器对该格式解析不正确,产生下载页面。

所以本平台采用另外一种手动设置的方式

	   this.getResponse().setContentType("text/html");
           this.getResponse().setCharacterEncoding("GBK");
           PrintWriter out = this.getResponse().getWriter();
           ajaxResult = "{error: '简历上传成功!但无法提取信息,请手动填写表单!',msg:'',filename:'"+ resumeFileFileName + "',filepath : ' " + targetDirectory+ "/" + targetFileName + " '}";
           out.write(ajaxResult);
           out.flush();

见 ResumeAction:111




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值