尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于Java+Spring+SSH的CRM客户关系管理系统设计与实现解析

基于Java+Spring+SSH的CRM客户关系管理系统设计与实现解析 简介本资源是一套基于Java技术栈开发的客户关系管理系统CRM完整实现方案面向计算机专业本科生、Java初学者及企业级Web应用开发者旨在帮助学习者掌握SSHStrutsSpringHibernate框架整合开发、JSP前端交互与业务逻辑分层设计等核心技能。压缩包共包含全套可运行源码与配套文档涵盖系统需求分析、数据库设计、模块功能说明及部署指南等内容所有代码均经测试校正确保一键导入IDE后可成功编译与运行。资源大小为86.92MB以Java源文件、JSP页面、XML配置文件、SQL脚本及PDF/DOCX格式文档为主结构清晰、注释完整便于理解MVC分层架构与事务管理机制。目前已有662人下载学习适合课程设计、毕业设计及中小型企业客户管理原型开发参考。1. 先把CRM的底账盘清楚这套系统到底做了什么拿到“基于JavaSpringSSH的CRM客户关系管理系统”这种题目很多人的第一反应是这不就是个老掉牙的JSP课设吗如果你只是这么想那后面写论文、讲PPT、应付答辩的时候一定会卡壳。我先说句实在话——这套系统之所以能在毕业设计里长盛不衰是因为它把Java Web最经典的分层架构、最常用的业务场景全都装进了一个完整闭环里。你把它跑通不难难的是把每一层为什么这么写、每个表为什么这么建都讲明白。1.1 从标题反推系统边界先拆解标题里最容易被忽视的信息。SSH不是Secure Shell而是Struts2 Spring Hibernate这三件套的缩写这是当年Java Web最主流的轻量级组合。后面跟的JSP是视图层技术负责渲染页面。整个链路就是浏览器请求 - Struts2拦截 - Action处理 - Spring管理对象 - Hibernate操作数据库 - 结果回填到JSP。所以你下载下来的zip包里源码结构必定是这几大块src目录Java源码按controller/service/dao/entity或者叫action/service/dao/model分包WebRoot或webapp目录JSP页面、JS、CSS、配置文件配置文件struts.xml、applicationContext.xml或spring配置文件、hibernate.cfg.xml数据库脚本通常是.sql文件里面是建库建表和初始数据文档开题报告、任务书、论文Word或PDF拿到这些材料后第一步不是双击运行而是先把包结构画成思维导图确认里面到底有哪些功能模块。判断一个CRM系统做得全不全就看五点客户信息管理、联系人管理、跟进记录、统计分析、用户权限。如果这套源码里五样都有那它的业务体量足够撑起一篇合格的毕业设计论文如果只有简单的增删改查那你要么自己加模块要么论文里侧重讲数据库设计和框架整合。1.2 业务模块全景拆解CRM的全称是Customer Relationship Management翻译过来是客户关系管理。你写论文的时候可以把它拔高成“企业以客户为中心、通过信息化手段优化销售流程”但落到代码层面它就是一桌子对数据库表格的增删改查。我建议你把业务模块拆成这么几块来理解首页仪表盘系统欢迎页通常显示当前登录用户、待办事项、近期客户统计等。这块用到的是聚合查询COUNT、GROUP BY做起来不复杂但很显工作量。客户管理核心模块。客户基本信息公司名、电话、邮箱、地址、所属地区、客户级别、客户来源的增删改查一般要支持按客户名模糊查询、分页显示。这是Hibernate CRUD的标准练手场景。联系人管理一个客户下有多个联系人采购、技术、财务这里是典型的一对多关联映射。数据库表现为t_customer和t_linkman两张表通过外键关联。跟进记录每次销售和客户沟通后记一笔文字描述加上下次跟进时间。这个模块最容易扩展加一个“待跟进提醒”功能就是创新点。数据统计按月份统计新增客户数、按来源统计客户比例。这里可以用HQL的group by子句前端用纯CSS画柱状图加简单的报表展示不需要引入图表框架。用户与权限管理员、普通销售、经理等角色。SSH框架下的权限控制通常用Spring AOP或者Struts2拦截器实现拦截未登录用户跳转到登录页。以上这些模块前三个是基础后两个是加分项。如果源码里缺了统计功能你完全能用HQL和JSP补出来这比新做一整个版块要省力得多。1.3 数据库表结构设计思路CRM系统的表不会特别多常规就是那么六七张t_user用户表id、用户名、密码、真实姓名、角色IDt_customer客户表id、客户名、联系人、电话、邮箱、地址、级别、来源、创建时间、创建人IDt_linkman联系人表id、客户ID、姓名、职位、电话、微信t_follow跟进记录表id、客户ID、跟进人、跟进内容、跟进时间、下次跟进时间t_role角色表id、角色名、备注表之间关系不算复杂但论文里值得认真画的图是E-R图。客户表与联系人表是一对多用户表与跟进记录表是一对多角色表与用户表是一对多。你只要把这三条线说清楚数据库设计这一章的配图就足够了。建表脚本通常放在项目根目录的db文件夹或者sql文件夹里。注意一点很多下载来的脚本是MySQL写的而且可能用了MyISAM引擎。建议你导入后改成InnoDB因为有外键关系的话InnoDB更靠谱。如果脚本里没写外键约束也不要紧代码层面用Hibernate的关联映射维护关系就够了这样做反而更灵活。2. 为什么是SSH给这套“老技术”一个新讲法现在网上到处都是Spring Boot、Spring Cloud你拿SSH做毕业设计如果老师问“为什么不选新技术”答不好容易翻车。我的建议是不要回避这个问题而是正面回答学习SSH的价值在于理解Java Web的分层思想。Spring Boot把很多东西自动化了而SSH需要你手动配置每一个Bean、每一对映射这个过程恰恰能证明你懂底层原理。2.1 Struts2、Spring、Hibernate各自的职责这三兄弟的分工可以用一个店面的运转来类比。Struts2是前台接待所有客人的动向都得经过它来路由——页面上点了一个“新增客户”的链接请求先到Struts2由struts.xml里的action映射决定交给哪个处理类。Spring是店长负责协调和调度——所有Action、Service、DAO对象的创建和依赖关系都由Spring管理不需要你到处new对象。Hibernate是仓库管理员——你跟它说“把一个客户对象存起来”它自动生成SQL操作数据库你不需要写繁琐的JDBC代码。具体到配置上你会看到struts.xml里配置action映射action namecustomer_* classcustomerAction method{1} result namelist/customer_list.jsp/result /action这样设计的意思是customer_list请求会调用CustomerAction的list方法成功后跳转到customer_list.jsp。通配符星号和{1}是Struts2省配置技巧很值得写进论文里讲。applicationContext.xml里配置Spring托管bean idcustomerAction classcom.crm.action.CustomerAction scopeprototype property namecustomerService refcustomerService/ /bean bean idcustomerService classcom.crm.service.CustomerService property namecustomerDao refcustomerDao/ /beanscopeprototype这个细节是在Struts2下必须设置的因为Action是有状态的多线程并发不能让多个请求共享同一个Action实例。你要是面试被问到或者答辩被问到这就是一个不错的亮点。Hibernate的映射文件了解即可实体类如Customer.java对应一张表配置文件里写字段与属性的对应关系。更简单的方式是直接用注解但SSH项目里用XML映射的居多能让你更直观看到ORM的过程。2.2 技术选型的“两难答辩话术”你肯定会面对一个问题“现在都用Spring Boot了你为什么还做SSH”直接回答“学校指定的”会被老师轻看。换个说法效果完全不同“SSH是Java Web里程碑式的轻量级组合。Struts2让我理解了MVC中控制层的请求流转机制Spring让我明白了IoC和AOP在项目里如何实际落地Hibernate让我体会到了ORM解决对象关系映射问题的思路。有了这个基础再去学Spring Boot处理起自动装配、事务管理、异常机制等问题会更加顺手。”这段话我在多个场合验证过老师普遍认这个说法。它包含三层意思你学懂了什么、做到了什么、为未来铺垫了什么。2.3 JSP在这套系统里的角色定位JSPJava Server Pages在SSH项目里承担视图层。它不是纯粹的HTML而是可以在页面中嵌入Java代码和Struts2标签。打开一个JSP页面你大概率会看到% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixs uri/struts-tags % html body h2客户列表/h2 s:iterator valuepageBean.list varc tr tds:property value#c.custName//td tda hrefcustomer_delete?id${c.id}删除/a/td /tr /s:iterator /body /html这里的s:iterator是Struts2的迭代标签作用类似于Java的for循环将后台传来的List遍历成表格行。这一行代码比你用Java脚本片段去循环要安全得多这也是Struts2标签库的核心优势。不过JSP有个小坑我当年踩过第一次修改页面后刷新没反应。原因是Tomcat默认开启了JSP编译缓存你需要把Eclipse或者IDEA里Server配置的“Out directory”清掉或者重启Tomcat。开发阶段可以在Tomcat的conf/web.xml里把JSP相关的reloadable改成true就是开机编译模式方便调试。3. 登录权限、客户管理、分页查询的代码级拆解既然标题明确写了设计与实现这篇博文的核心还是得落到代码上。我不打算把整份源码贴一遍那没意义。我挑四个最能体现技术含量、同时也是答辩最容易问到的关键点来讲。3.1 用户登录与权限控制登录功能看着简单但涉及密码加密、Session管理、Struts2拦截器技术层次是够的。一般代码流程是public class LoginAction extends ActionSupport { private String username; private String password; private UserService userService; public String execute() throws Exception { User user userService.login(username, password); if (user null) { this.addActionError(用户名或密码错误); return ERROR; } // 存入Session ServletActionContext.getRequest().getSession() .setAttribute(loginUser, user); return SUCCESS; } // 省略getter/setter }关键在于登录的逻辑放在Service层还是Dao层。规范做法是Service层判断因为密码校验属于业务规则。如果是纯查询走DAO然后在Action内比较也能跑但论文里讲分层时会显得不够严谨。还有一个重要的点不登录能不能访问页面答案是不能。Struts2的拦截器就是干这个的。写一个拦截器类public class LoginInterceptor extends AbstractInterceptor { public String intercept(ActionInvocation invocation) throws Exception { MapString, Object session invocation.getInvocationContext().getSession(); if (session.get(loginUser) null) { return login; // 跳转到登录页 } return invocation.invoke(); // 放行 } }然后在struts.xml里注册这个拦截器并将其设为默认拦截器同时排除登录Actionpackage namedefault extendsstruts-default interceptors interceptor namecheckLogin classcom.crm.interceptor.LoginInterceptor/ interceptor-stack namemyStack interceptor-ref namedefaultStack/ interceptor-ref namecheckLogin/ /interceptor-stack /interceptors default-interceptor-ref namemyStack/ action namelogin_* classloginAction method{1} interceptor-ref namedefaultStack/ result namelogin/login.jsp/result /action /package这里的关键是登录Action自身要跳过拦截检查否则会形成死循环还没登录就永远跳转登录页永远无法登录。这是我见过最多新手犯的错。3.2 客户管理的分页实现客户表数据量大之后一页显示所有记录是灾难。页面要分页显示常见的是PageBean工具类。你在源码里大概率能找到类似这样一个类public class PageBean { private int currentPage; // 当前页码 private int totalPage; // 总页数 private int pageSize; // 每页条数 private int totalCount; // 总记录数 private List? list; // 当前页数据 }业务逻辑分两步先查总记录数再查当前页数据。在DAO里用Hibernate的Criteria或HQL来实现// 查询总记录数 String hql select count(*) from Customer; Long total (Long) session.createQuery(hql).uniqueResult(); // 查询当前页数据 int start (currentPage - 1) * pageSize; Query query session.createQuery(from Customer); query.setFirstResult(start); query.setMaxResults(pageSize); ListCustomer list query.list();setFirstResult和setMaxResults是Hibernate实现物理分页的入口底层会根据数据库方言生成对应的limit语句MySQL或rownum语句Oracle。这里有一个细节值得写进论文如果数据量很大Hibernate的count*查询不会缓存实际性能会受表数据量影响但不影响毕业设计层面的展示效果。分页做出来之后页面上需要有上一页、下一页、总页数、总条数的导航。这一步需要把PageBean对象塞到request或session里JSP再用EL表达式取。3.3 多条件组合查询客户列表页通常有搜索框按客户名模糊查、按客户级别查、按所属地区查。组合起来的难点在于不确定用户填了几个条件。稳定做法是动态拼接HQLpublic ListCustomer findByCondition(Customer condition) { StringBuilder hql new StringBuilder(from Customer c where 11 ); if (StringUtils.isNotBlank(condition.getCustName())) { hql.append(and c.custName like :custName ); } if (StringUtils.isNotBlank(condition.getCustLevel())) { hql.append(and c.custLevel :custLevel ); } Query query session.createQuery(hql.toString()); if (StringUtils.isNotBlank(condition.getCustName())) { query.setParameter(custName, % condition.getCustName() %); } if (StringUtils.isNotBlank(condition.getCustLevel())) { query.setParameter(custLevel, condition.getCustLevel()); } return query.list(); }这里where 11是关键技巧目的是让后面每个条件都能安全地用and拼接不需要再判断哪个条件是第一个条件。很多同学会觉得写“11”有点丑但在动态SQL里它非常实用。这类细节如果能在文档里讲清楚论文查重时也能提升内容的原创性。3.4 文件上传导出Excel给论文加亮点如果你的源码里没有文件上传、Excel导出这两个功能我强烈建议自己补上。原因很简单毕业设计答辩时老师很爱问“你这个系统有没有导入导出的功能”。有的话你演示一版给老师看说明你是真做了效果比任何技术名词都好。文件上传在SSH里用的是commons-fileupload组件配置好struts.multipart.parser后Action中直接接收File类型的属性即可public class UploadAction extends ActionSupport { private File upload; // 待上传文件 private String uploadContentType; // 文件类型 private String uploadFileName; // 文件名 public String execute() throws Exception { String path ServletActionContext.getServletContext() .getRealPath(/upload); File dir new File(path); if (!dir.exists()) dir.mkdirs(); File dest new File(path File.separator uploadFileName); FileUtils.copyFile(upload, dest); return SUCCESS; } }Excel导出建议用POI毕业设计只做最简单的数据导出不需要复杂格式。代码量不多但往论文里一写就是加分项。4. 部署跑通全流程从压缩包到浏览器能看到页面很多人卡在这步。下载的源码打开报一堆红叉或者运行时总是404。我按经验整理一个比较稳妥的顺序。4.1 环境准备你需要JDK版本匹配。SSH项目老源码很矫情JDK 1.8是大多数项目的安全选择如果源码用了较老的CGLIB库里Hibernate 3.x时代的内容JDK版本过高可能会遇到“Unable to load class”之类的问题。JDK 1.8配合最高到Tomcat 9的版本没问题不要一上来用JDK 17配合Tomcat 10因为Tomcat 10起用了Jakarta命名空间SSH老项目不兼容如果你用的是Eclipse这里说一个隐藏点Web项目部署到Tomcat时会存在“Web App Libraries”依赖的问题。如果lib目录下明明有jar包但运行时提示ClassNotFound多半是项目没有把lib目录下的依赖发布到WEB-INF/lib。解决方式是右键项目 - Properties - Deployment Assembly将src和WebRoot添加进列表。4.2 数据库配置打开src目录下的jdbc.properties或applicationContext.xml里的数据源配置正常是这几行jdbc.driverClassNamecom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/crm_db?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password你的密码把数据库名改成你自己的库名用数据库客户端跑一遍.sql脚本。这里有个坑脚本里的字符集如果是utf8而你本机MySQL是utf8mb4导入后中文可能会乱码。建议导入前手动执行set names utf8mb4; 再source脚本。4.3 发布到Tomcat把项目打成war包或者直接用开发工具部署。确认几个点Tomcat的server.xml里Connector的URIEncoding设置为UTF-8不然页面提交的中文会有乱码JSP页面顶部要有charsetUTF-8Hibernate生成表时如果表名或字段名是中文需要在数据库连接上加useUnicodetrue启动后访问http://localhost:8080/项目名/如果看到登录页说明部署成功。看不懂源码逻辑的时候就先用系统把所有能点的按钮都点一遍记录功能点后面写文档和测试用例用得上。4.4 验证一条完整链路我强烈建议拿到系统后先跑通一条“从登录到新增客户到编辑再到删除”的完整链路。这一步能最快帮你认识系统。流程是用管理员账号登录进入客户管理页面搜索默认数据中是否存在客户如果有初始化数据新增一条客户记录填写公司名、联系人、电话查询列表是否出现这条记录修改记录里的某个字段刷新页面看是否生效删除该记录确认列表消失每操作一步你就在心里对应一下代码里哪个Action处理了请求、哪个方法执行了SQL这个映射建立起来之后整个项目对你来说不再是一个黑盒。这也让后面在论文上写“系统测试”那一章变得特别轻快你不用编测试结论每一个步骤都是你亲手验证过的。5. 常见问题与排查技巧实录这部分都是经验我见过的坑全写这里。5.1 部署启动阶段问题现象可能原因解决办法Tomcat启动报ClassNotFound依赖jar包没部署到WEB-INF/lib清理项目发布配置把lib加入Deployment Assembly启动过程中大量报错但服务可用Hibernate的c3p0连接池没配置好检查数据库和表是否存在或修改连接参数Struts2报Unable to load configurationstruts.xml里class属性指向的类不存在检查源码包的类路径与配置一致页面报404项目名不对或web.xml里welcome-file错误用完整路径访问比如crm/user_login.action中文乱码页面编码与请求编码不一致统一使用UTF-8修改Tomcat的URIEncoding5.2 运行阶段项目能启动、页面能打开并不等于万事大吉。我遇到过以下一些典型的运行问题登录成功后跳转404。action结果配成了redirect但没有指定命名空间。你打开struts.xml查找result标签确认type是dispatcher转发还是redirect重定向。如果跳转路径没带.action后缀很容易404。查询列表空白。排除数据库里没数据的情况后绝大多数是Service层返回了空listAction里没把list放入request作用域或者JSP遍历的变量名跟setAttribute的key对不上。检查Action里的写法例如request.setAttribute(list, list); 而在JSP里应该这样取s:iterator value#request.list。有些源代码里会把list放进ValueStack直接valuelist取也行但要注意和Action的属性名对应。删除操作提示外键约束失败。这说明有联系人、跟进记录引用客户表的数据在删除客户前需要先删除关联数据或者在Hibernate的映射里配置cascadedelete。修改客户实体类Customer.hbm.xml里的set标签加上cascadedelete。Hibernate的懒加载异常在页面遍历客户的同时访问客户下的联系人列表偶发LazyInitializationException。解决办法是在web.xml里配置OpenSessionInView过滤器或者把关联集合的lazy改为false。不过后者不推荐作为默认方案因为会影响所有查询的性能。还有一类问题很隐蔽重启项目后第一次访问很慢。这是因为Hibernate在启动时构建SessionFactory表映射过多的项目会更明显。这不是Bug不用紧张。5.3 源码中一些容易“翻车”的安全细节虽然这是课程设计或者项目练习但既然要写进文档顺便说说代码里的安全问题。我见过很多SSH项目的登录密码是明文存储的这当然不推荐但你可以在文档里把它作为“后期改进方向”反而能体现你考虑周全。另外JSP页面里如果出现下面这种脚本片段% String username (String) session.getAttribute(username); if (username null) { response.sendRedirect(login.jsp); } %说明权限控制写在页面里不够安全。你可以提一句实际项目中权限校验应该放在Action拦截器或Spring AOP层避免请求绕过页面直接访问后端。这类话放在论文“不足与展望”里比空喊几句口号有价值得多。5.4 一个特别值得写的创新点如果老师要你加东西或者你想提高论文的区分度我推荐在“跟进记录提醒”上做文章。设计思路很简单在登录后的首页仪表盘中增加一个查询“当前登录用户有哪些客户的下次跟进时间小于等于今天”SQL大概是String hql from Follow f where f.followUser.id :userId and f.nextTime :today and f.isOver 0;把结果渲染在首页待办清单里。这个功能不需要新表、不需要复杂算法但业务价值高因为“管理客户 管理跟进节奏”是CRM的核心逻辑。答辩时可以这样讲“本系统的创新点在于引入了待跟进提醒机制让销售人员在打开系统时第一时间获取需要跟进的客户列表。”简单、落地、有说服力。6. 源码阅读路线与文档写作顺序拿到zip里的源码不要毫无头绪地翻我建议按下面这个顺序过一遍效率最高。6.1 从web.xml开始web.xml是整个Web应用的入口。你打开它会看到Spring的ContextLoaderListener、Struts2的FilterDispatcher以及欢迎页和字符集过滤器。按照配置文件的指引去逐个看对应的Java类比随机点开每个文件高效得多。建议只看核心配置和核心Action其他冗余代码按模块扫一眼即可。6.2 按“用户 - 客户 - 联系人 - 跟进”的顺序读代码先用用户登录流程把整个分层走一遍LoginAction - UserService - UserDao - User.hbm.xml - 数据库。再去看客户模块。这样一条线走下来你对SSH整合的流程就清楚了后面看联系人、跟进记录时节奏会很快。6.3 文档写作顺序建议很多人的做法是先写代码再写文档最后一天晚上抄模板。这个思路反了我建议的写作顺序是先画功能结构图系统有哪些模块、哪些页面、哪些按钮再画技术架构图浏览器层 - Struts2控制层 - Spring业务层 - Hibernate持久层接着画E-R图表之间什么关系、主外键如何设计然后写数据库设计文档每张表的字段说明最后写“核心功能实现”结合你刚跑的完整链路把类和关键配置贴上去加几句解释这样的写作顺序是顺着真实设计流程走的论文结构看起来就非常自然。6.4 文档中如何写“设计思想”论文里最容易写得干巴巴的部分是“系统的设计思想”。我给你一个写法参考“本系统采用SSH框架严格按照MVC模式分层。控制层由Struts2负责接收用户请求并调用业务层业务层由Spring管理负责事务控制和业务逻辑持久层使用Hibernate完成对象关系映射降低数据库操作的复杂度。视图层使用JSP结合Struts2标签库页面与业务逻辑分离使系统具有良好的扩展性和可维护性。”这段话给你打底你完全可以按自己系统实际情况微调但别直接照抄——查重系统不是吃素的。适当替换几个词描述落到你自己项目的类名和方法名上就会变成你的“设计思想”。7. 几点私货关于这套代码我的真实感受写到这里我知道很多人看完这篇长文后真正的问题是这套系统值不值得我花时间把它吃透我的看法是这样。如果你手里已经拿到了这份源码那它其实不只是用来交差的东西。它包含了一个Java Web学习者从入门到熟练的必经之路你能在里面看到经典的分层看到老牌框架之间的协作看到真实的业务模块不是玩具级。哪怕你将来要做Spring Boot项目很多思路是相通的。我自己印象很深的场景是早年在阅读别人的SSH项目时经常被struts.xml和applicationContext.xml里面各种id引用搞得晕头转向。后来我养成了一个习惯拿到任何一套框架型源码第一时间画一张“配置引用关系表”比如customerAction依赖customerServicecustomerService依赖customerDaocustomerDao依赖SessionFactorySessionFactory依赖DataSource。把这十来条引用链理顺整个项目就透明了。你可以现在就拿这张表去对照你的源码画一遍。另外你千万别把源码里的代码复制粘贴到自己的论文里就完事。我不反对参考但至少要改掉类名、变量名、界面文字最重要的是要能讲清楚每段代码的意思。答辩的时候老师一眼就能看出来哪些是你写的、哪些是你抄的。真正把项目跑一遍、改一个功能比如加个查询条件、在文档里留下你操作的截图这从安全性和稳妥性的角度来说比任何“创新点”都靠谱。最后再叮嘱一句拿到源码后数据库一定要先备份一份原始数据。我见过太多人把测试数据删得七零八落最后恢复不回来。留着原始备份出问题可以随时倒回去这算是这套系统操作中最值得记住的一条经验了。本文还有配套的精品资源点击获取
返回列表