基于SSH框架开发企业级CRM客户管理系统

一.需求分析

基于SSH框架开发企业级CRM客户管理系统_第1张图片

主UI:

基于SSH框架开发企业级CRM客户管理系统_第2张图片


二.服务架构

采用单服务架构,后期的博客会将该项目扩展到服务集群和分布式缓存系统架构,敬请期待。

基于SSH框架开发企业级CRM客户管理系统_第3张图片

三.数据表设计

部分E-R图:

基于SSH框架开发企业级CRM客户管理系统_第4张图片

用户表:

CREATE TABLE `sys_user` (
  `user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `user_code` varchar(32) NOT NULL COMMENT '用户账号',
  `user_name` varchar(64) NOT NULL COMMENT '用户名称',
  `user_password` varchar(32) NOT NULL COMMENT '用户密码',
  `user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

客户表:

CREATE TABLE `cst_linkman` (
  `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
  `lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
  `lkm_cust_id` bigint(32) NOT NULL COMMENT '客户id',
  `lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
  `lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
  `lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
  `lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
  `lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
  `lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
  `lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
  PRIMARY KEY (`lkm_id`),
  KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
  CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

联系人表:

CREATE TABLE `cst_linkman` (
  `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
  `lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
  `lkm_cust_id` bigint(32) NOT NULL COMMENT '客户id',
  `lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
  `lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
  `lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
  `lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
  `lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
  `lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
  `lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
  PRIMARY KEY (`lkm_id`),
  KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
  CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

客户拜访记录表:

CREATE TABLE `sale_visit` (
  `visit_id` varchar(32) NOT NULL,
  `visit_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id',
  `visit_user_id` bigint(32) DEFAULT NULL COMMENT '负责人id',
  `visit_time` date DEFAULT NULL COMMENT '拜访时间',
  `visit_interviewee` varchar(32) DEFAULT NULL COMMENT '被拜访人',
  `visit_addr` varchar(128) DEFAULT NULL COMMENT '拜访地点',
  `visit_detail` varchar(256) DEFAULT NULL COMMENT '拜访详情',
  `visit_nexttime` date DEFAULT NULL COMMENT '下次拜访时间',
  PRIMARY KEY (`visit_id`),
  KEY `FK_sale_visit_cust_id` (`visit_cust_id`),
  KEY `FK_sale_visit_user_id` (`visit_user_id`),
  CONSTRAINT `FK_sale_visit_cust_id` FOREIGN KEY (`visit_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `FK_sale_visit_user_id` FOREIGN KEY (`visit_user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

客户信息表:

CREATE TABLE `cst_customer_detail` (
  `cust_id` bigint(32) NOT NULL,
  `cust_region` varchar(64) DEFAULT NULL COMMENT '客户地区',
  `cust_zip` varchar(16) DEFAULT NULL COMMENT '客户邮政编码',
  `cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址',
  `cust_fax` varchar(64) DEFAULT NULL COMMENT '客户传真',
  `cust_website` varchar(128) DEFAULT NULL COMMENT '客户网址',
  `cust_licence` varchar(64) DEFAULT NULL COMMENT '客户营业执照注册号',
  `cust_corporation` varchar(64) DEFAULT NULL COMMENT '企业法人',
  `cust_capital` bigint(16) DEFAULT NULL COMMENT '客户注册资金',
  `cust_bank` varchar(512) DEFAULT NULL COMMENT '开户银行及账号',
  `cust_pic` varchar(64) DEFAULT NULL COMMENT '客户资质图片',
  `cust_memo` longtext COMMENT '客户简介',
  PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
数据字典表:
CREATE TABLE `base_dict` (
  `dict_id` varchar(32) NOT NULL COMMENT '数据字典id(主键)',
  `dict_type_code` varchar(10) NOT NULL COMMENT '数据字典类别代码',
  `dict_type_name` varchar(64) NOT NULL COMMENT '数据字典类别名称',
  `dict_item_name` varchar(64) NOT NULL COMMENT '数据字典项目名称',
  `dict_item_code` varchar(10) DEFAULT NULL COMMENT '数据字典项目(可为空)',
  `dict_sort` int(10) DEFAULT NULL COMMENT '排序字段',
  `dict_enable` char(1) NOT NULL COMMENT '1:使用 0:停用',
  `dict_memo` varchar(64) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`dict_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
用户角色表:
CREATE TABLE `sys_role` (
  `role_id` bigint(32) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(32) NOT NULL COMMENT '角色名称',
  `role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
CREATE TABLE `sys_user_role` (  
  `role_id` bigint(32) NOT NULL COMMENT '角色id',  
  `user_id` bigint(32) NOT NULL COMMENT '用户id',  
  PRIMARY KEY (`role_id`,`user_id`),  
  KEY `FK_user_role_user_id` (`user_id`),  
  CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,  
  CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

四.程序编写

4.1 项目引入Spring4, Hibernate, Struts2三大框架。

     导入SSH所需的jar

    基于SSH框架开发企业级CRM客户管理系统_第5张图片

     在WEB-INF文件夹下新建一个web.xml文件,配置如下:



  luyuan_crm
  
    index.html
    index.htm
    index.jsp
    default.html
    default.htm
    default.jsp
  
  
  
  
    org.springframework.web.context.ContextLoaderListener
  
  
  
  
    contextConfigLocation
    classpath:applicationContext.xml
  
  
  
  
  	OpenSessionInViewFilter
  	org.springframework.orm.hibernate5.support.OpenSessionInViewFilter
   
  
  
  	OpenSessionInViewFilter
  	*.action
  
  
   
  
    struts2
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  
  
    struts2
    /* 
  

在SRC下,新建applicationContext.xml,将Hibernate和Struts2交给Spring管理。




	
	
	
	
	 
		
		
		
		
	
	
	
	
		
		
		
		
			
				org.hibernate.dialect.MySQLDialect
				true
				true
				update
			
		
		
		
		
			
				com/luyuan/crm/domain/User.hbm.xml
			
		
	
	
	
	
		
	
	
	
	

	

新建jdbc数据库配置文件jdbc.properties。

jdbc.driverClass=com.mysql.jdbc.Driver  
jdbc.url=jdbc:mysql:///crm   #要连接的数据库名称
jdbc.username=root 
jdbc.password=root

引入日志记录配置文件log4j.properties,这个去拷贝apache log4j配置文件即可。

新建struts.xml配置文件。




	
	
	
	
		
	

至此,SSH的基本配置完成的差不多了,可以正式进行项目的开发。

4.2.搭建项目基本骨架

基于SSH框架开发企业级CRM客户管理系统_第6张图片

创建BaseDao.java,定义常用的几个通用接口,一般为增删改查,因为一般表都需要CRUD操作。

/**
 * 通用的DAO的接口
 * @author hp
 *
 * @param 
 */
public interface BaseDao {
	
	public void save(T t);
	
	public void update(T t);
	
	public void delete(T t);
	
	public T getById(Serializable id);
	
	// 查询所有
	public List getAll();
	
	//统计个数的方法
	public Integer getCount(DetachedCriteria detachedCriteria);
	
	//分页查询
	public List getByPage(DetachedCriteria detachedCriteria, Integer begin, Integer pageSize);
}

BaseDaoImpl.java


由于这里需要知道表映射成的实体类型的class, 需要用反射的方式获取该泛型中的T的具体class类型。可以采用在无参构造中获取。

/**
 * 通用的DAO的实现类
 * @author hp
 *
 * @param 
 */
public class BaseDaoImpl extends HibernateDaoSupport implements BaseDao{

	private Class clazz;
	
	public BaseDaoImpl() {
		// 反射:第一步获得Class
		Class clazz = this.getClass();// 正在被调用那个类的Class,CustomerDaoImpl或者LinkManDaoImpl。
		// 查看JDK的API
		Type type = clazz.getGenericSuperclass();// 参数化类型:BaseDaoImpl,BaseDaoImpl
		// 得到这个type就是一个参数化的类型, 将type强转成参数化的类型:
		ParameterizedType pType = (ParameterizedType) type;
		// 通过参数化类型获得实际类型参数:得到一个实际类型参数的数组?Map.
		Type[] types = pType.getActualTypeArguments();
		// 只获得第一个实际类型参数即可。
		this.clazz = (Class) types[0];// 得到Customer、LinkMan、User
	}
	
	@Override
	public void save(T t) {
		this.getHibernateTemplate().save(t);
	}

	@Override
	public void update(T t) {
		this.getHibernateTemplate().update(t);
	}

	@Override
	public void delete(T t) {
		this.getHibernateTemplate().delete(t);
	}

	@Override
	public T getById(Serializable id) {
		return (T) this.getHibernateTemplate().get(clazz, id);
	}

	@Override
	public List getAll() {	         
		return (List) this.getHibernateTemplate().find("from " + clazz.getSimpleName()); 
	}

	@Override
	public Integer getCount(DetachedCriteria detachedCriteria) {
		// 设置统计个数的条件:
		detachedCriteria.setProjection(null); //清除条件的缓存
		detachedCriteria.setProjection(Projections.rowCount());
		List counts = (List) this.getHibernateTemplate().findByCriteria(detachedCriteria);
		if(null != counts && counts.size() > 0) {
			return counts.get(0).intValue();
		}
		return null;
	}

	@Override
	public List getByPage(DetachedCriteria detachedCriteria, Integer begin, Integer pageSize) {
		detachedCriteria.setProjection(null);
		return (List) this.getHibernateTemplate().findByCriteria(detachedCriteria, begin, pageSize);
	}

	

}

4.3 登录注册模块

注册界面:

基于SSH框架开发企业级CRM客户管理系统_第7张图片

登录界面:

基于SSH框架开发企业级CRM客户管理系统_第8张图片

创建实体和表的映射

创建实体:

public class User {
	private long user_id;
	private String user_code;
	private String user_name;
	private String user_password;
	private String user_state;
	
	public long getUser_id() {
		return user_id;
	}
	public void setUser_id(long user_id) {
		this.user_id = user_id;
	}
	public String getUser_code() {
		return user_code;
	}
	public void setUser_code(String user_code) {
		this.user_code = user_code;
	}
	public String getUser_name() {
		return user_name;
	}
	public void setUser_name(String user_name) {
		this.user_name = user_name;
	}
	public String getUser_password() {
		return user_password;
	}
	public void setUser_password(String user_password) {
		this.user_password = user_password;
	}
	public String getUser_state() {
		return user_state;
	}
	public void setUser_state(String user_state) {
		this.user_state = user_state;
	}
	
	
}

创建映射文件User.hbm.xml:


	
		
			
		
		
		
		
		
		
	

DAO层创建UserDao.java

public interface UserDao extends BaseDao{
	//注册调用父类的save()
	
	//登录
	User login(User user);
	
	//账户是否已经被注册
	boolean isRegeisted(User user);
}

实现类UserDaoImpl.java

public class UserDaoImpl extends BaseDaoImpl implements UserDao {

	@Override
	public User login(User user) {
		List users = (List) this.getHibernateTemplate().find("from User where user_code=? "
				+ "and user_password=?", user.getUser_code(),
				user.getUser_password());
		if(null != users && users.size() > 0) {
			return users.get(0);
		}
		return null;
	}

	@Override
	public boolean isRegeisted(User user) {
		List users = (List) this.getHibernateTemplate().find("from User where user_code=?", 
				user.getUser_code());
		if(null != users && users.size() > 0) {
			return true;
		}
		return false;
	}

	
}

业务service层创建UserService.java接口

public interface UserService {
	void regeist(User user);
	
	User login(User user);
}

实现类UserServiceImpl.java

//因为涉及保存操作,必须开启事务,不然Hibernate默认only-read
@Transactional
public class UserServiceImpl implements UserService {
	
	//注入Dao
	private UserDao userDao;
	
	public void setUserDao(UserDao mUserDao) {
		this.userDao = mUserDao;
	}

	@Override
	public void regeist(User user) {
		//先查询该账户是否已经注册
		if(!userDao.isRegeisted(user)) {
			user.setUser_password(MD5Utils.md5(user.getUser_password()));
			user.setUser_state("1");
			userDao.save(user);
		} 
	}

	@Override
	public User login(User user) {
		//密码是密文传输的
		user.setUser_password(MD5Utils.md5(user.getUser_password()));
		return userDao.login(user);
	}

	
}

并在applicationContext.xml添加相关Dao:


	
		
	
在web.action包下创建UserAction.java
public class UserAction extends ActionSupport implements ModelDriven {
	
	//注入UserService
	private UserService userService;

	public void setUserService(UserService mUserService) {
		this.userService = mUserService;
	}

	// 模型驱动使用的对象
	private User user = new User();
	
	@Override
	public User getModel() {
		return user;
	}
	
	
	//注册
	public String regeist() {
		userService.regeist(user);
		return LOGIN;
	}
	
	//登录
	public String login() {
		// 调用业务层查询用户
		User existUser = userService.login(user);
		if(null == existUser) {
			// 登录失败
			//添加错误信息
			this.addActionError("用户名或密码错误!");
			return LOGIN;
		} else {
			//登录成功,将用户信息持久化
			ActionContext.getContext().getSession().put("existUser", existUser);
			return SUCCESS;
		}
	}
}

并在applicationContext.xml添加相关Service:


	
		
	
	
	
	
	
		
	

在struts.xml添加下列配置:


		
			/login.jsp
			/index.html
		

测试注册:

基于SSH框架开发企业级CRM客户管理系统_第9张图片

查询数据库,添加用户成功:


测试登录:

登录失败

基于SSH框架开发企业级CRM客户管理系统_第10张图片

登录成功,跳转到首页

基于SSH框架开发企业级CRM客户管理系统_第11张图片

4.4 客户管理模块

列表页

基于SSH框架开发企业级CRM客户管理系统_第12张图片

添加页

基于SSH框架开发企业级CRM客户管理系统_第13张图片

创建实体和表的映射

创建实体:

public class Customer {
	private Long cust_id;
	private String cust_name;
	private String cust_source;
	private String cust_industry;
	private String cust_level;
	private String cust_phone;
	private String cust_mobile;
        //设置器和访问器...
}
创建映射文件Customer.hbm.xml:

	
		
			
		
		
		
		
		
		
		
		
		
	

然后在applicationContext.xml引入该映射文件


		
			
				com/luyuan/crm/domain/User.hbm.xml
				com/luyuan/crm/domain/Customer.hbm.xml
			
		

创建CustomerDao.java

public interface CustomerDao extends BaseDao {

}
CustomerDaoImpl.java
public class CustomerDaoImpl extends BaseDaoImpl implements CustomerDao {

}

业务层Service

创建CustomerService.java

/**
 * 客户管理的Service的接口
 * @author hp
 *
 */
public interface CustomerService {
	void save(Customer customer);

	PageBean getByPage(DetachedCriteria detachedCriteria, Integer currPage,Integer pageSize);

	Customer getById(Long cust_id);

	void delete(Customer customer);

	void update(Customer customer);

	List getAll();
}

创建CustomerServiceImpl.java

public class CustomerServiceImpl implements CustomerService {

	// 注入客户的DAO
	private CustomerDao customerDao;

	public void setCustomerDao(CustomerDao customerDao) {
		this.customerDao = customerDao;
	}
		
	// 业务层保存客户的方法:
	@Override
	public void save(Customer customer) {
		customerDao.save(customer);
	}

	// 业务层根据ID查询客户的方法
	@Override
	public Customer getById(Long cust_id) {
		return customerDao.getById(cust_id);
	}

	@Override
	public void delete(Customer customer) {
		customerDao.delete(customer);
	}

	@Override
	public void update(Customer customer) {
		customerDao.update(customer);
	}

	@Override
	public List getAll() {
		return customerDao.getAll();
	}
	
	// 业务层分页查询客户的方法:
	@Override
	public PageBean getByPage(DetachedCriteria detachedCriteria, Integer currPage, Integer pageSize) {
		PageBean pageBean = new PageBean<>();
		//封装当前页数
		pageBean.setCurrPage(currPage);
		//封装每页显示的条数
		pageBean.setPageSize(pageSize);
		//封装总条数
		int totalCount = customerDao.getCount(detachedCriteria);
		pageBean.setTotalCount(totalCount);
		//封装总页数
		int totalPage = (int) Math.ceil(totalCount * 1.0 / pageSize) ;
		pageBean.setTotalPage(totalPage);
		// 封装每页显示数据的集合
		int begin = (currPage - 1) * pageSize;
		List data = customerDao.getByPage(detachedCriteria, begin, pageSize);
		pageBean.setList(data);
		return pageBean;
	}

}

创建CustomerAction.java

public class CustomerAction extends ActionSupport implements ModelDriven{

	//模型驱动使用的对象
	private Customer customer = new Customer();
	
	@Override
	public Customer getModel() {
		return customer;
	}

	//注入Service
	private CustomerService customerService;

	public void setCustomerService(CustomerService customerService) {
		this.customerService = customerService;
	}
	
	// 接收分页数据:提供了两个参数名称:page 代表当前页数 和 rows 每页显示记录数
	private Integer page = 1;
	private Integer rows = 8;

	public void setPage(Integer page) {
		if (page == null) {
			page = 1;
		}
		this.page = page;
	}

	public void setRows(Integer rows) {
		if (rows == null) {
			rows = 8;
		}
		this.rows = rows;
	}
	
	/**
	 * 分页查询客户的方法
	 * @return
	 * @throws IOException 
	 */
	public String getByPage() throws IOException {
		// 创建离线条件查询
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
		//调用业务层
		PageBean pageBean = customerService.getByPage(detachedCriteria, page, rows);
		// 使用gson转成json
		HashMap map = new HashMap();
		map.put("total", pageBean.getTotalCount());
		map.put("rows", pageBean.getList());
		String json = new Gson().toJson(map);
		System.out.println("json:" + json);
		ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
		ServletActionContext.getResponse().getWriter().println(json);
		return NONE;
	}
	
	/**
	 * 编写save方法:
	 * 
	 * @throws IOException
	 */
	public String save() throws IOException {
		Map map = new HashMap();
		try {
			// 调用业务层:
			customerService.save(customer);
			map.put("msg", "保存成功!");
		} catch (Exception e) {
			e.printStackTrace();
			map.put("msg", "保存失败!");
		}
		//换一种方式转json, 使用JSONlib转成JSON:
		JSONObject jsonObject = JSONObject.fromObject(map);
		System.out.println(jsonObject.toString());
		ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
		ServletActionContext.getResponse().getWriter().println(jsonObject.toString());// {"msg":..}
		return NONE;
	}
	
	public String getById() throws IOException {
		customer = customerService.getById(customer.getCust_id());
		ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
		ServletActionContext.getResponse().getWriter().println(new Gson().toJson(customer));
		return NONE;
	}

	public String update() throws IOException {
		Map map = new HashMap();
		try {
			// 调用业务层:
			customerService.update(customer);
			map.put("msg", "保存成功!");
		} catch (Exception e) {
			e.printStackTrace();
			map.put("msg", "保存失败!");
		}
		ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
		ServletActionContext.getResponse().getWriter().println(new Gson().toJson(map));
		return NONE;
	}

	public String delete() throws IOException {
		Map map = new HashMap();
		try {
			// 调用业务层:
			customer = customerService.getById(customer.getCust_id());
			customerService.delete(customer);
			map.put("msg", "保存成功!");
		} catch (Exception e) {
			e.printStackTrace();
			map.put("msg", "保存失败!");
		}
		
		ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
		ServletActionContext.getResponse().getWriter().println(new Gson().toJson(map));

		return NONE;
	}
}
未完待续。。。



你可能感兴趣的:(java)