https://github.com/ChenCurry/ssh_crm
(SSH框架,eclipse,jdk7,tomcat7)
在前面SSH框架整合的基础之上,进行功能实现,专注技术应用
客户列表开发
分页查询后端代码抽取,各种不同类型的分页查询可以复用代码
controller层
CustomerAction
import org.apache.commons.lang3.StringUtils; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Restrictions; import cn.itcast.domain.Customer; import cn.itcast.service.CustomerService; import cn.itcast.utils.PageBean; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class CustomerAction extends ActionSupport implements ModelDriven<Customer> { private Customer customer = new Customer(); private CustomerService cs; private Integer currentPage; private Integer pageSize; public String list() throws Exception { //封装离线查询对象 DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); //判断并封装参数 if(StringUtils.isNotBlank(customer.getCust_name())){ dc.add(Restrictions.like("cust_name", "%"+customer.getCust_name()+"%")); } //1 调用Service查询分页数据(PageBean) PageBean pb = cs.getPageBean(dc,currentPage,pageSize); //2 将PageBean放入request域,转发到列表页面显示 ActionContext.getContext().put("pageBean", pb); return "list"; } @Override public Customer getModel() { return customer; } public void setCs(CustomerService cs) { this.cs = cs; } public Integer getCurrentPage() { return currentPage; } public void setCurrentPage(Integer currentPage) { this.currentPage = currentPage; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } }
domain层
PageBean
import java.util.List; public class PageBean { //当前页数 private Integer currentPage; //总记录数 private Integer totalCount; //每页显示条数 private Integer pageSize; //总页数 private Integer totalPage; //分页列表数据 private List list; public PageBean(Integer currentPage, Integer totalCount, Integer pageSize) { this.totalCount = totalCount; this.pageSize = pageSize; this.currentPage = currentPage; if(this.currentPage == null){ //如页面没有指定显示那一页.显示第一页. this.currentPage = 1; } if(this.pageSize == null){ //如果每页显示条数没有指定,默认每页显示3条 this.pageSize = 3; } //计算总页数 this.totalPage = (this.totalCount+this.pageSize-1)/this.pageSize; //判断当前页数是否超出范围 //不能小于1 if(this.currentPage < 1){ this.currentPage = 1; } //不能大于总页数 if(this.currentPage > this.totalPage){ this.currentPage = this.totalPage; } } //计算起始索引 public int getStart(){ return (this.currentPage-1)*this.pageSize; } public Integer getCurrentPage() { return currentPage; } public void setCurrentPage(Integer currentPage) { this.currentPage = currentPage; } public Integer getTotalCount() { return totalCount; } public void setTotalCount(Integer totalCount) { this.totalCount = totalCount; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } }
service层
CustomerService
import org.hibernate.criterion.DetachedCriteria; import cn.itcast.utils.PageBean; public interface CustomerService { //分页业务方法 PageBean getPageBean(DetachedCriteria dc, Integer currentPage, Integer pageSize); }
CustomerServiceImpl
import java.util.List; import org.hibernate.criterion.DetachedCriteria; import cn.itcast.dao.CustomerDao; import cn.itcast.domain.Customer; import cn.itcast.service.CustomerService; import cn.itcast.utils.PageBean; public class CustomerServiceImpl implements CustomerService { private CustomerDao cd; @Override public PageBean getPageBean(DetachedCriteria dc, Integer currentPage, Integer pageSize) { //1 调用Dao查询总记录数 Integer totalCount = cd.getTotalCount(dc); //2 创建PageBean对象 PageBean pb = new PageBean(currentPage, totalCount, pageSize); //3 调用Dao查询分页列表数据 List<Customer> list = cd.getPageList(dc,pb.getStart(),pb.getPageSize()); //4 列表数据放入pageBean中.并返回 pb.setList(list); return pb; } public void setCd(CustomerDao cd) { this.cd = cd; } }
dao层
CustomerDao
import cn.itcast.domain.Customer; public interface CustomerDao extends BaseDao<Customer> { }
CustomerDaoImpl
import java.util.List; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Projections; import org.springframework.orm.hibernate5.support.HibernateDaoSupport; import cn.itcast.dao.CustomerDao; import cn.itcast.domain.Customer; public class CustomerDaoImpl extends BaseDaoImpl<Customer> implements CustomerDao { }
BaseDao
import java.io.Serializable; import java.util.List; import org.hibernate.criterion.DetachedCriteria; public interface BaseDao<T> { //增 想要传入T泛型, 接口名称上需要声明 传入int需要用Serilizable void save(T t); //删 void delete(T t); //删 void delete(Serializable id); //改 void update(T t); //查 根据id查询 T getById(Serializable id); //查 符合条件的总记录数 Integer getTotalCount(DetachedCriteria dc); //查 查询分页列表数据 List<T> getPageList(DetachedCriteria dc,Integer start,Integer pageSize); }
BaseDaoImpl
import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.util.List; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Projections; import org.springframework.orm.hibernate5.support.HibernateDaoSupport; import cn.itcast.dao.BaseDao; public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> { private Class clazz;//用于接收运行期泛型类型 public BaseDaoImpl() { //获得当前类型的带有泛型类型的父类 ParameterizedType ptClass = (ParameterizedType) this.getClass().getGenericSuperclass(); //获得运行期的泛型类型 clazz = (Class) ptClass.getActualTypeArguments()[0]; } @Override public void save(T t) { getHibernateTemplate().save(t); } @Override public void delete(T t) { getHibernateTemplate().delete(t); } @Override public void delete(Serializable id) { T t = this.getById(id);//先取,再删 getHibernateTemplate().delete(t); } @Override public void update(T t) { getHibernateTemplate().update(t); } @Override public T getById(Serializable id) { return (T) getHibernateTemplate().get(clazz, id); } @Override public Integer getTotalCount(DetachedCriteria dc) { //设置查询的聚合函数,总记录数 dc.setProjection(Projections.rowCount()); List<Long> list = (List<Long>) getHibernateTemplate().findByCriteria(dc); //清空之前设置的聚合函数 dc.setProjection(null); if(list!=null && list.size()>0){ Long count = list.get(0); return count.intValue(); }else{ return null; } } @Override public List<T> getPageList(DetachedCriteria dc, Integer start, Integer pageSize) { List<T> list = (List<T>) getHibernateTemplate().findByCriteria(dc, start, pageSize); return list; } }
数据字典
Customer
public class Customer { /* * CREATE TABLE `cst_customer` ( `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)', `cust_name` VARCHAR(32) NOT NULL COMMENT '客户名称(公司名称)', `cust_source` VARCHAR(32) DEFAULT NULL COMMENT '客户信息来源', `cust_industry` VARCHAR(32) DEFAULT NULL COMMENT '客户所属行业', `cust_level` VARCHAR(32) DEFAULT NULL COMMENT '客户级别', `cust_linkman` VARCHAR(64) DEFAULT NULL COMMENT '联系人', `cust_phone` VARCHAR(64) DEFAULT NULL COMMENT '固定电话', `cust_mobile` VARCHAR(16) DEFAULT NULL COMMENT '移动电话', PRIMARY KEY (`cust_id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; */ private Long cust_id; private String cust_name; //private String cust_source; //private String cust_industry; //private String cust_level; private String cust_linkman; private String cust_phone; private String cust_mobile; //引用关联的数据字典对象 private BaseDict cust_source; //客户来源 cust_source.dict_id private BaseDict cust_industry; //客户行业 private BaseDict cust_level; //客户级别 public BaseDict getCust_source() { return cust_source; } public void setCust_source(BaseDict cust_source) { this.cust_source = cust_source; } public BaseDict getCust_industry() { return cust_industry; } public void setCust_industry(BaseDict cust_industry) { this.cust_industry = cust_industry; } public BaseDict getCust_level() { return cust_level; } public void setCust_level(BaseDict cust_level) { this.cust_level = cust_level; } public Long getCust_id() { return cust_id; } public void setCust_id(Long cust_id) { this.cust_id = cust_id; } public String getCust_name() { return cust_name; } public void setCust_name(String cust_name) { this.cust_name = cust_name; } public String getCust_linkman() { return cust_linkman; } public void setCust_linkman(String cust_linkman) { this.cust_linkman = cust_linkman; } public String getCust_phone() { return cust_phone; } public void setCust_phone(String cust_phone) { this.cust_phone = cust_phone; } public String getCust_mobile() { return cust_mobile; } public void setCust_mobile(String cust_mobile) { this.cust_mobile = cust_mobile; } @Override public String toString() { return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]"; } }
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.domain" > <class name="Customer" table="cst_customer" > <id name="cust_id" > <!-- generator:主键生成策略(明天讲) --> <generator class="native"></generator> </id> <property name="cust_name" column="cust_name" ></property> <!-- <property name="cust_source" column="cust_source" ></property> <property name="cust_industry" column="cust_industry" ></property> <property name="cust_level" column="cust_level" ></property> --> <property name="cust_linkman" column="cust_linkman" ></property> <property name="cust_phone" column="cust_phone" ></property> <property name="cust_mobile" column="cust_mobile" ></property> <!-- 多对一 --> <many-to-one name="cust_source" column="cust_source" class="BaseDict" ></many-to-one> <many-to-one name="cust_industry" column="cust_industry" class="BaseDict" ></many-to-one> <many-to-one name="cust_level" column="cust_level" class="BaseDict" ></many-to-one> </class> </hibernate-mapping>
实现下拉选
后台
BaseDictAction

package cn.itcast.web.action; import java.util.List; import net.sf.json.JSONArray; import org.apache.struts2.ServletActionContext; import cn.itcast.domain.BaseDict; import cn.itcast.service.BaseDictService; import com.opensymphony.xwork2.ActionSupport; public class BaseDictAction extends ActionSupport { private String dict_type_code; private BaseDictService baseDictService; @Override public String execute() throws Exception { //1 调用Service根据typecode获得数据字典对象list List<BaseDict> list = baseDictService.getListByTypeCode(dict_type_code); //2 将list转换为 json格式 String json = JSONArray.fromObject(list).toString(); //3 将json发送给浏览器 ServletActionContext.getResponse().setContentType("application/json;charset=utf-8"); ServletActionContext.getResponse().getWriter().write(json); return null;//告诉struts2不需要进行结果处理 } public String getDict_type_code() { return dict_type_code; } public void setDict_type_code(String dict_type_code) { this.dict_type_code = dict_type_code; } public void setBaseDictService(BaseDictService baseDictService) { this.baseDictService = baseDictService; } }
BaseDictService

import java.util.List; import cn.itcast.domain.BaseDict; public interface BaseDictService { //根据根据数据字典类型字段获得数据字典对象 List<BaseDict> getListByTypeCode(String dict_type_code); }
BaseDictServiceImpl

import java.util.List; import cn.itcast.dao.BaseDictDao; import cn.itcast.domain.BaseDict; import cn.itcast.service.BaseDictService; public class BaseDictServiceImpl implements BaseDictService { private BaseDictDao bdd; @Override public List<BaseDict> getListByTypeCode(String dict_type_code) { return bdd.getListByTypeCode(dict_type_code); } public void setBdd(BaseDictDao bdd) { this.bdd = bdd; } }
BaseDictDao

import java.util.List; import cn.itcast.domain.BaseDict; public interface BaseDictDao extends BaseDao<BaseDict> { //根据类型获得数据字典列表 List<BaseDict> getListByTypeCode(String dict_type_code); }
BaseDictDaoImpl

import java.util.List; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Restrictions; import cn.itcast.dao.BaseDictDao; import cn.itcast.domain.BaseDict; public class BaseDictDaoImpl extends BaseDaoImpl<BaseDict> implements BaseDictDao { @Override public List<BaseDict> getListByTypeCode(String dict_type_code) { // Criteria // 创建离线查询对象 DetachedCriteria dc = DetachedCriteria.forClass(BaseDict.class); // 封装条件 dc.add(Restrictions.eq("dict_type_code", dict_type_code)); // 执行查询 List<BaseDict> list = (List<BaseDict>) getHibernateTemplate().findByCriteria(dc); return list; } }
前台
my.js
//使用ajax加载数据字典,生成select //参数1: 数据字典类型 (dict_type_code) //参数2: 将下啦选放入的标签id //参数3: 生成下拉选时,select标签的name属性值 //参数4: 需要回显时,选中哪个option function loadSelect(typecode,positionId,selectname,selectedId){ //1 创建select对象,将name属性指定 var $select = $("<select name="+selectname+" ></select>"); //2 添加提示选项 $select.append($("<option value='' >---请选择---</option>")); //3 使用jquery 的ajax 方法,访问后台Action $.post("${pageContext.request.contextPath}/BaseDictAction", { dict_type_code:typecode}, function(data){ //遍历 //4 返回json数组对象,对其遍历 $.each( data, function(i, json){ // 每次遍历创建一个option对象 var $option = $("<option value='"+json['dict_id']+"' >"+json["dict_item_name"]+"</option>"); if(json['dict_id'] == selectedId){ //判断是否需要回显 ,如果需要使其被选中 $option.attr("selected","selected"); } //并添加到select对象 $select.append($option); }); },"json"); //5 将组装好的select对象放入页面指定位置 $("#"+positionId).append($select); }
add.jsp
<script type="text/javascript"> $(document).ready(function(){ loadSelect("006","level","cust_level.dict_id" <s:if test="#customer.cust_level!=null">,<s:property value="#customer.cust_level.dict_id" /></s:if>); loadSelect("001","industry","cust_industry.dict_id" <s:if test="#customer.cust_industry!=null">,<s:property value="#customer.cust_industry.dict_id" /></s:if>); loadSelect("009","source","cust_source.dict_id" <s:if test="#customer.cust_source!=null">,<s:property value="#customer.cust_source.dict_id" /></s:if>); }); </script>
保存客户
CustomerAction.java-->CustomerService.java-->CustomerServiceImpl.java-->BaseDao.java-->BaseDaoImpl.java
struts2文件上传
前台格式
<!-- 文件上传页面3个要求: 1.表单必须post提交 2.表单提交类型enctype.必须多段式. 3.文件上传使用<input type="file" /> 组件 --> <FORM id=form1 name=form1 action="${pageContext.request.contextPath }/CustomerAction_add" method="post" enctype="multipart/form-data" >
后台接收
//上传的文件会自动封装到File对象 //在后台提供一个与前台input type=file组件 name相同的属性 private File photo; //在提交键名后加上固定后缀FileName,文件名称会自动封装到属性中 private String photoFileName; //在提交键名后加上固定后缀ContentType,文件MIME类型会自动封装到属性中 private String photoContentType; public String add() throws Exception { if(photo!=null){ System.out.println("文件名称:"+photoFileName); System.out.println("文件类型:"+photoContentType); //将上传文件保存到指定位置 photo.renameTo(new File("E:/upload/haha.jpg")); } //--------------------------------------------------------------------- //1 调用Service,保存Customer对象 cs.save(customer); //2 重定向到客户列表Action return "toList"; }
客户修改功能
用 新增功能的后台+新增功能的前端改造 即可实现
联系人功能
add.jsp,list.jsp,edit.jsp-->struts.xml-->LinkManAction.java-->LinkManService.java-->LinkManServiceImpl.java-->BaseDao.java-->BaseDaoImpl.java
用户注册
regist.jsp-->struts.xml-->UserAction.java-->UserService.java-->UserServiceImpl.java-->BaseDao.java-->BaseDaoImpl.java-->login.jsp
登陆校验拦截器
PrivilegeInterceptor.java
import java.util.Map; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; public class PrivilegeInterceptor extends MethodFilterInterceptor { @Override //不校验登陆和注册方法 protected String doIntercept(ActionInvocation invocation) throws Exception { //1 获得Session Map<String, Object> session = ActionContext.getContext().getSession(); //2 获得登陆标识 User user = (User) session.get("user"); //3 判断标识是否存在 if(user != null){ //存在=> 放行 return invocation.invoke(); }else{ //不存在=> 重定向到登陆页面 return "toLogin"; } } }
struts.xml
<interceptors> <!-- 注册拦截器 --> <interceptor name="privilegeInterceptor" class="cn.itcast.web.interceptor.PrivilegeInterceptor"></interceptor> <!-- 配置拦截器栈 --> <interceptor-stack name="myStack"> <interceptor-ref name="privilegeInterceptor"> <param name="excludeMethods">login,regist</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 指定默认拦截器栈 --> <default-interceptor-ref name="myStack"></default-interceptor-ref> <!-- 全局结果集配置 --> <global-results> <result name="toLogin" type="redirect" >/login.jsp</result> </global-results>
员工拜访客户功能(其中,ORM的设计需要好好理解)
拜访记录的添加/客户拜访记录列表/客户拜访记录修改
add.jsp,list.jsp-->struts.xml-->SaleVisitAction.java-->SaleVisitService.java-->SaleVisitServiceImpl.java-->SaleVisitDao.java-->SaleVisitDaoImpl.java-->BaseDao.java-->BaseDaoImpl.java
客户行业统计
menu.jsp-->struts.xml-->CustomerAction.java-->CustomerService.java-->CustomerServiceImpl.java-->CustomerDao.java-->CustomerDaoImpl.java
Spring注解替代xml配置
<!-- 开启扫描类中的注解 --> <context:component-scan base-package="cn.itcast"></context:component-scan>
原配置中需要干掉的如下
<!-- 配置通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager" > <tx:attributes> <tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" /> <tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" /> <tx:method name="*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> </tx:attributes> </tx:advice> <!-- 配置将通知织入目标对象 配置切点 配置切面 --> <aop:config> <aop:pointcut expression="execution(* cn.itcast.service.impl.*ServiceImpl.*(..))" id="txPc"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" /> </aop:config> <!-- action --> <!-- 注意:Action对象作用范围一定是多例的.这样才符合struts2架构 --> <bean name="userAction" class="cn.itcast.web.action.UserAction" scope="prototype" > <property name="userService" ref="userService" ></property> </bean> <bean name="customerAction" class="cn.itcast.web.action.CustomerAction" scope="prototype" > <property name="cs" ref="customerService" ></property> </bean> <bean name="baseDictAction" class="cn.itcast.web.action.BaseDictAction" scope="prototype" > <property name="baseDictService" ref="baseDictService" ></property> </bean> <bean name="linkManAction" class="cn.itcast.web.action.LinkManAction" scope="prototype" > <property name="lms" ref="linkManService" ></property> </bean> <bean name="saleVisitAction" class="cn.itcast.web.action.SaleVisitAction" scope="prototype" > <property name="svs" ref="saleVisitService" ></property> </bean> <!-- service --> <bean name="userService" class="cn.itcast.service.impl.UserServiceImpl" > <property name="ud" ref="userDao" ></property> </bean> <bean name="customerService" class="cn.itcast.service.impl.CustomerServiceImpl" > <property name="cd" ref="customerDao" ></property> </bean> <bean name="baseDictService" class="cn.itcast.service.impl.BaseDictServiceImpl" > <property name="bdd" ref="baseDictDao" ></property> </bean> <bean name="linkManService" class="cn.itcast.service.impl.LinkManServiceImpl" > <property name="lmd" ref="linkManDao" ></property> </bean> <bean name="saleVisitService" class="cn.itcast.service.impl.SaleVisitServiceImpl" > <property name="svd" ref="saleVisitDao" ></property> </bean> <!-- dao --> <bean name="userDao" class="cn.itcast.dao.impl.UserDaoImpl" > <property name="sessionFactory" ref="sessionFactory" ></property> </bean> <bean name="customerDao" class="cn.itcast.dao.impl.CustomerDaoImpl" > <property name="sessionFactory" ref="sessionFactory" ></property> </bean> <bean name="baseDictDao" class="cn.itcast.dao.impl.BaseDictDaoImpl" > <property name="sessionFactory" ref="sessionFactory" ></property> </bean> <bean name="linkManDao" class="cn.itcast.dao.impl.LinkManDaoImpl" > <property name="sessionFactory" ref="sessionFactory" ></property> </bean> <bean name="saleVisitDao" class="cn.itcast.dao.impl.SaleVisitDaoImpl" > <property name="sessionFactory" ref="sessionFactory" ></property> </bean>
注解如下
dao层
@Repository("saleVisitDao") public class SaleVisitDaoImpl extends BaseDaoImpl<SaleVisit> implements SaleVisitDao { @Resource(name="sessionFactory") public void setSF(SessionFactory sf){ super.setSessionFactory(sf); } }
service层
@Service("saleVisitService") @Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false) public class SaleVisitServiceImpl implements SaleVisitService { @Resource(name="saleVisitDao") private SaleVisitDao svd;
controller层
@Controller("saleVisitAction") @Scope("prototype") public class SaleVisitAction extends ActionSupport implements ModelDriven<SaleVisit>{ private SaleVisit saleVisit = new SaleVisit(); @Resource(name="saleVisitService") private SaleVisitService svs ;
EasyUI
将前端改造成EasyUi界面(略)