GRT公司客户关系管理系统(CRM)项目总结

到GRT公司有近3个月了,做CRM系统也两月有余了。


虽然这个系统饱受我们三个程序员的诟病,但还是有不少值得学习的地方。至少它的架构不是我这种水平的人能搭建的了的(该系统的架构师后跳槽至阿里巴巴)。


据说会做总结是个好习惯,需要养成。故在此即将离开GRT公司时(还未获批),花点时间,对该项目(算是我的第一个正式应用在生产环境的项目),主要是技术方面,进行总结。如下:


  • 主要功能需求:该项目是GRT公司目前内部使用的,主要用来管理公司(集成商)与客户、用户单位、供应商、集成商、合作伙伴等之间的关系,以及带有审批流程的各商机、立项项目、执行项目等的系统化,自动化管理。该项目基础架构和主要功能已经在第一次开发(08年-09年)中完成。我们的工作就是更新组织架构,修复系统Bug,以及完成“企管部”提出的新需求。
  • 基本技术架构:Struts1.2  + Spring2.5 + Spring JDBC (jBPM3.2部分采用Hibernate作持久层框架)。
  • 界面方面:采用ExtJs+自定义标签库的方式做页面框架。
  • 采用Quartz做任务调度,完成定期给指定人生成指定任务的工作。首先引入quartz.jar,然后在Spring的配置文件中添加:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "spring-beans.dtd" > <beans> <!-- 要调度的对象--> <bean id="testQuarz" class="com.scgreat.crm.util.QuartzTimmer" ></bean> <!--定义定时执行testQuarz 这个bean中的sayHello()方法--> <bean id="helloworldTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <ref bean="testQuarz" /> </property> <property name="targetMethod"> <value>trackingTaskAssign</value> </property> </bean> <!--触发器的bean的设置,在这里我们设置了我们要触发的jobDetail是哪个。这里我们定义了要触发的jobDetail是helloworldTask,即触发器去触发哪个bean..并且我们还定义了触发的时间:每天5:17pm--> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail"> <ref bean="helloworldTask" /> </property> <property name="cronExpression"> <!-- 关键在配置此表达式--> <value>0 20 12 * * ?</value> <!--DEBUG<value>0 * * * * ?</value>--> </property> </bean> <!--管理触发器的总设置,管理我们的触发器列表,可以在bean的list中放置多个触发器。 --> <bean autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref local="cronTrigger" /> </list> </property> </bean> </beans>
  • 增加此配置后,系统将利用反射机制自动按照Cron Expression配置的时间如0 20 12 * * ?,对类com.scgreat.crm.util.QuartzTimmer调用方法trackingTaskAssign。
  • 后台代码架构:采用接口模式;包结构最上层按模块划分,分为CRM(业务逻辑),System(系统:用户、角色、机构、权限),jBPM(工作流),Email以及一个核心模块(定义了一系列的抽象类、接口,以供继承,包括BaseBean,BaseBlg,BaseDao,BaseObject,Exception,分页模块,一系列基于apache.commons.xxUtils的通用辅助类,还有一个叫SpringHelper的Spring容器辅助类)。
  • SpringHelper是Spring的辅助类:包括初始化Spring容器及Action层(不由Spring管理)用getBean(name)的方式获得Spring管理的对象。该系统中Spring不是在启动web时初始化,是当访问第一个Action时发生的。Action调用SpringHelper的静态方法getBean(name)之后才在构造SpringHelper时初始化了Spring容器。我认为这个方法不好:不如让Spring添加在web.xml配置文件中,然后让所有action继承一个在构造方法中有自我注入功能的基类来的方便。更能充分发挥Spring的作用。
  • 每个模块,然后按照实体类(bean),业务逻辑层(blg),持久化层(Dao),展现层(WebApp)以及一个工具包(Util)分为多个包。包下再分为接口层和实现层。
  • 这个项目让我收获最大的是对jBPM强大功能,以及相关的动态表单技术的初步了解。
  • 该系统权限管理较细。细到每个按钮都有权限的控制。在页面上的按钮都是自定义标签库的代码根据数据库中的按钮和权限生成的。
  • 自定义标签库:如这句(<dict:checkbox dictCode="device" dictType="C" name="devices" css="" />)由tld文件定义为<tag> <description>checkbox HTML 代码生成标签</description> <name>checkbox</name> <tag-class>com.scgreat.dict.webapp.taglib.CheckBoxTag</tag-class> <body-content>empty</body-content> <attribute> <description>数据字典类型,默认为“C”(通用数据字典)</description> <name>dictType</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>数据字典代码</description> <name>dictCode</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>下拉列表项名称</description> <name>name</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>下拉列表项值</description> <name>value</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>样式(默认为“form_select”)</description> <name>css</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>只读(默认为“false”)</description> <name>readonly</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>不可用(默认为“false”)</description> <name>disabled</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>附加样式</description> <name>style</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>事件类型</description> <name>eventType</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description>事件响应函数</description> <name>eventHandler</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
     
  • com.scgreat.dict.webapp.taglib.CheckBoxTag文件为

/* * SelectTag.java 1.0, 2009-3-19 下午05:34:43 * * Copyright 2009 SC-GREAT, Inc. All rights reserved. * SC-GREAT PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.scgreat.dict.webapp.taglib; import java.util.List; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import com.scgreat.core.util.StringEscapeUtils; import com.scgreat.core.util.StringUtils; import com.scgreat.core.webapp.taglib.BaseTag; import com.scgreat.dict.Constants; import com.scgreat.system.bean.BusiParamBean; import com.scgreat.system.blg.BusiParamBlg; /** * 下拉列表 HTML 代码生成标签 * * @author lipinliang * @version 1.0, 2009-3-19 下午05:34:43 * @since JDK1.4 * @see com.scgreat.dict.Constants */ public class SelectTag extends BaseTag { private static final long serialVersionUID = 1L; /** * 数据字典类型,默认为“C”(通用数据字典) */ private String dictType = Constants.DICT_TYPE_COMMON; /** * 数据字典代码 */ private String dictCode = null; /** * 下拉列表项名称 */ private String name = null; /** * 下拉列表项值 */ private String value = null; /** * 只读(默认为“false”) */ private boolean readonly = false; /** * 不可用(默认为“false”) */ private boolean disabled = false; /** * 样式(默认为“form_select”) */ private String css = "form_select"; /** * 附加样式 */ private String style = null; /** * 事件类型 */ private String eventType = null; /** * 事件响应函数 */ private String eventHandler = null; public void setDictType(String dictType) { this.dictType = dictType; } public void setDictCode(String dictCode) { this.dictCode = dictCode; } public void setName(String name) { this.name = name; } public void setValue(String value) { this.value = value; } public void setReadonly(boolean readonly) { this.readonly = readonly; } public void setDisabled(boolean disabled) { this.disabled = disabled; } public void setCss(String css) { this.css = css; } public void setStyle(String style) { this.style = style; } public void setEventType(String eventType) { this.eventType = eventType; } public void setEventHandler(String eventHandler) { this.eventHandler = eventHandler; } public int doStartTag() { return SKIP_BODY; } public int doEndTag() throws JspException { // 校验参数 if (StringUtils.isBlank(this.dictCode)) { return EVAL_PAGE; } this.name = StringUtils.isBlank(this.name) ? this.dictCode : this.name; try { JspWriter out = this.pageContext.getOut(); // 树形数据字典 if (this.dictType.equalsIgnoreCase(Constants.DICT_TYPE_TREE)) { // TODO } // 高级数据字典 else if (this.dictType.equalsIgnoreCase(Constants.DICT_TYPE_ADVANCED)) { // TODO } // 通用数据字典 else { BusiParamBlg busiParamBlg = (BusiParamBlg) this.getBean("busiParamBlg"); List<BusiParamBean> dictCommonItems = busiParamBlg.getBusiParams(this.dictCode); out.write("<select id=/""); out.write(this.name); out.write("/""); out.write(" name=/""); out.write(this.name); out.write("/""); out.write(" class=/""); out.write(this.css); out.write("/""); if(StringUtils.isNotBlank(this.style)) { out.write(" style="/" mce_style="/"""); out.write(this.style); out.write("/""); } if(this.readonly) { out.write(" readonly=/"readonly/""); } if(this.disabled) { out.write(" disabled=/"disabled/""); } if (StringUtils.isNotBlank(this.eventType) && StringUtils.isNotBlank(this.eventHandler)) { out.write(" " + this.eventType + "=/"" + this.eventHandler + "(this.value);/""); } out.write(">/n"); if (dictCommonItems.isEmpty()) { out.write("<option value=/"/">尚未设置字典项</option>/n"); } else { for (BusiParamBean busiParam : dictCommonItems) { String key = busiParam.getItemKey(); out.write("<option value=/""); out.write(key); if(StringUtils.isNotBlank(this.value) && key.trim().equals(this.value.trim())) { out.write("/" selected=/"selected/">"); } else { out.write("/">"); } out.write(StringEscapeUtils.escapeHtml(busiParam.getItemValue())); out.write("</option>/n"); } } out.write("</select>"); } } catch (Exception e) { log.error("下拉列表 HTML 代码生成失败!", e); } return SKIP_BODY; } public void release() { super.release(); this.dictType = "C"; this.dictCode = null; this.name = null; this.value = null; this.readonly = false; this.disabled = false; this.css = "form_select"; this.style = null; this.eventType = null; this.eventHandler = null; } }

  • 时序图GRT公司客户关系管理系统(CRM)项目总结_第1张图片


 

未完待续。。(下回添加项目结构图,以及对系统的详细解释,和技术细节部分的说明)

你可能感兴趣的:(GRT公司客户关系管理系统(CRM)项目总结)