SSH框架CRM客户关系管理系统课设开发全解析

📅 发布时间:2026/8/30 6:35:00
SSH框架CRM客户关系管理系统课设开发全解析 简介本资源是一套基于Java技术栈开发的客户关系管理系统CRM完整实现方案面向高校计算机专业课程设计、毕业设计及Java Web开发初学者解决企业客户信息管理、销售流程跟踪与服务响应等典型业务场景建模与系统落地问题。压缩包共含全套可运行源码与配套文档大小为86.92MB涵盖JSP前端页面、Spring与SSHStruts2SpringHibernate三层架构后端代码、MySQL数据库脚本及详细设计说明书、部署说明与测试记录各模块职责清晰便于理解MVC分层逻辑与框架整合要点。已有662人学习下载源码经实测可一键部署运行附带完整业务流程客户录入、跟进记录、合同管理、统计报表等与规范注释特别适合用于巩固SSH框架集成、掌握传统Java Web开发全流程及撰写课程设计报告。 做Java Web课程设计的人几乎都会碰到这样一个题目客户关系管理系统也就是CRM。市面上的参考项目一抓一大把但代码质量只能说一言难尽——要么是框架整合层的XML配置乱成一团要么是JSP页面里塞满了Java脚本片段要么是数据库表设计干脆就是一锅粥。我前阵子帮一个学弟完整过了一遍基于SpringStrutsHibernate也就是Java Web领域常说的SSH的CRM客户关系管理系统源码和配套文档花了两天时间把架构逻辑、核心表、请求流转和部署链路全部捋了一遍最后发现这个老技术栈的项目其实非常值得拿来当教材正面是它把分层架构和框架整合讲得很清楚负面是它把经典SSH项目里很多让人抓狂的坑也全部暴露了出来。这篇就借这个CRM项目把我梳理过程中的完整思路、关键设计和踩坑记录整理出来给正在做课设、或者想把这个题目做成毕设的同学一个可直接参考的路线图。1. 为什么2025年还要拆一个SSH老项目需求定位与系统全貌1.1 这个CRM到底管理什么客户、跟进与商机CRM字面意思是客户关系管理但放到课设里最常见的业务边界其实是围绕三件事管住客户信息、记录跟进过程、统计销售结果。这三个动作听上去简单落地到系统里却需要一整套数据结构和页面流支撑。先说客户信息。一个客户档案不只是公司名称和联系方式还应该包含客户来源百度投放、朋友转介绍、展会、电销名单……、所属行业、客户等级A/B/C/D、当前状态潜在、意向、成交、流失等维度。这些字段不是摆设它们直接决定了后续所有统计报表的口径。比如要算这个月新增了多少A级客户前提就是客户表和等级字段设计得足够规范。再说跟进记录。这是CRM和普通通讯录软件最大的区别。一个客户从线索到成交中间可能要经过三五次甚至十几次的电话、面谈、微信沟通每一次沟通都应该留下记录并且下次跟进时间要能被系统提醒。跟进记录表本质上是一个带时间轴的流水表每次操作都插入一条新记录而不是覆盖旧记录——这一点很多初学者会搞错觉得我更新一下客户的描述不就行了结果客户沟通历史全丢了。最后是商机管理。商机可以理解成可能成交的单子它挂在客户下面包含产品名称、预计金额、预计成交日期、销售阶段初次接触、方案报价、商务谈判、赢单/输单。商机表和客户表是一对多关系一个客户名下可以有多个商机但一个商机只能归属一个客户。这套模型做出来之后我手上正在跟进多少金额的商机就能直接通过SQL聚合算出来这也是CRM系统最有说服力的功能之一。1.2 角色与权限谁在用这套系统CRM的使用者通常有三类角色。销售专员负责录入客户、记跟进、建商机销售主管要能看到团队所有成员的数据做业绩统计和分配系统管理员负责维护用户账号、配置基础数据字典。这套权限逻辑落到项目里最常见的实现方式是做一个简单的角色字段在用户表里加一个role属性然后通过拦截器去判断当前登录用户能访问哪些Action。想再规范一点就上RBAC基于角色的访问控制五张表用户表、角色表、菜单表、用户角色关联表、角色菜单关联表。课设阶段用简单的角色字段已经足够但如果你想让答辩更有亮点把RBAC做进去是一个性价比很高的加分项。1.3 模块清单与技术选型逻辑按照上面的业务拆解一个完整的SSH版CRM系统通常包含以下模块系统管理用户、角色、菜单、客户管理客户列表、新增、编辑、删除、分配、联系人管理、跟进记录管理、商机管理、数据统计按阶段统计客户数量、按月份统计成交金额、销售排行榜、日程提醒今日需跟进的客户。技术选型方面本来Spring Boot SSM现在已经是绝对主流为什么还要用SSH最直接原因是很多学校的课程体系还停留在SSH阶段另一个原因是SSH的三层拆分方式把表现层、业务层、持久层的概念呈现得特别清晰,学完再看Spring Boot会更容易理解约定大于配置到底省了什么。所以如果你想把这个项目做得既符合课设要求又能为后面Spring Boot学习打基础SSH反而是个不错的中间站。2. SSH三件套的真正分工Spring容器、Struts2控制层与Hibernate持久层2.1 请求从JSP到数据库的完整链路先跟我走一遍完整的请求链路这是理解SSH项目的钥匙。用户在JSP页面点击保存客户表单以POST方式提交请求URL是/customer_save。这个URL会先被web.xml里配置的Struts2过滤器拦下由Struts2框架根据配置文件的映射规则找到对应的Action类例如com.crm.web.action.CustomerAction。这里是第一层接力。然后是Spring容器。Struts2的Action本身并不直接new出来而是通过Spring的插件去容器里获取。为什么要这样因为Action里要调用Service接口Service又依赖Dao接口Dao依赖Hibernate的SessionFactory这一长串依赖如果全部手动new代码会耦合得没法看。Spring通过依赖注入把Service实例塞进Action的属性里Action只认接口根本不用关心实现类是怎么创建的。再往下一层是Service调用Dao。Dao层基于HibernateTemplate或者Hibernate的Session完成数据库操作把结果返回给ServiceService包装成页面需要的数据结构返回给ActionAction再把数据放到值栈ValueStack中最终通过Struts2的结果类型跳转到JSP页面渲染。这个链路里每一层都有明确的边界Action只做参数接收、调用和跳转控制Service只做业务逻辑比如保存客户前先校验手机号是否重复Dao只做增删改查。很多SSH项目写烂了根本原因是有人把SQL直接写到了Action里面或者Service里直接操作Session分层名存实亡。2.2 Spring为什么是整合的胶水SSH里最关键的配置文件是applicationContext.xml它是Spring IoC容器的核心。这个文件里通常配置了三类东西数据源、SessionFactory、Service和Action的Bean定义。数据源我建议用C3P0连接池配置方式很简单指定数据库驱动、URL、用户名、密码再设置最小连接数和最大连接数。SessionFactory交给Spring管理后Hibernate的主配置文件hibernate.cfg.xml甚至可以不要把方言、显示SQL、自动建表这些属性统统丢到applicationContext.xml里用property标签配。最容易被忽略的是事务配置。在SSH项目里Service层的方法通常需要使用Transactional注解或者通过AOP配置声明式事务。不配事务会出现什么情况保存客户的时候同时写客户表和跟进记录表第二步抛异常但客户表已经提交了——这就是数据不一致。Spring管理Hibernate事务本质上是对当前线程的Session做了绑定只有绑定了事务的Session才能在失败时回滚。这个知识点答辩很喜欢问一定要能讲清楚。2.3 Hibernate懒加载与Session关闭顺序的经典拼图SSH整合时有一个高频报错叫LazyInitializationException字面意思是懒加载初始化异常。Hibernate的懒加载是用到的时候才去数据库查但如果Session已经关了再去查关联对象就会抛这个异常。我见过很多学弟用暴力解法把Session关闭时间延长到视图渲染完OpenSessionInView模式。但生产环境里这种模式会长时间占用数据库连接并发一上来连接池就炸了。更合理的做法是在Service层就完成所有关联数据的初始化比如查询客户列表时如果页面要显示每个客户最近的跟进时间就在查询客户的同时用HQL join fetch把跟进记录查出来或者直接用一条子查询计算。这样Action层拿到的已经是一个完整的数据对象Hibernate的Session闭不关闭都不影响。我个人的建议是在课设阶段最省心的是把一个客户的联系人、跟进记录做成查询方法里显式初始化配合DTO返回。虽然代码会多一点但逻辑清楚而且面试的时候你说得出为什么要这么干比写一堆OpenSessionInView配置要有说服力得多。3. 数据库建模一套可跑通全部业务的表设计3.1 客户与联系人一对多还是多对多客户和联系人的关系理论上一个客户可以有多个联系人一个联系人可能需要关联多个客户比如一个采购人同时对接两家供应商但课程设计阶段做成一对多就够了否则中间表的CRUD会把你绕晕。一对多的表设计非常直观客户主表crm_customer联系人表crm_contact联系人表里有个customer_id外键指向客户主键。客户主表的核心字段我列一下id主键自增、name客户名称、level客户等级、source来源渠道、industry行业、status客户状态、phone联系电话、owner_user_id归属销售、create_time创建时间、update_time更新时间。这里的owner_user_id直接关联用户表用于区分这个客户是谁的列表页就能按当前登录人过滤。3.2 跟进记录时间线设计的关键跟进记录表crm_follow_up是最能体现业务理解的一张表。它的核心字段包括id、customer_id关联客户、contact_id本次沟通的联系人、content沟通内容、next_time下次跟进时间、create_by跟进人、create_time跟进时间。这里有一个很关键的细节跟进记录表的每条记录都代表一次独立的沟通事件存储的是event不是state。所以更新客户状态的时候应该是客户端状态变化跟进记录insert一条新数据而不是update原有的跟进内容。下次跟进时间next_time一定要单独存因为统计今日需跟进客户全靠这个字段千万不能从跟进内容里去解析。页面展示时跟进记录通常是按create_time倒序排列的时间线。对应的HQL写法大概是from FollowUp f where f.customer.id ? order by f.createTime desc用HQL而不是SQL是因为Hibernate可以直接操作对象属性省去手动映射ResultSet的机械劳动。3.3 RBAC权限表的落地方式如果决定做RBAC表结构是固定的五张核心表。用户表sys_user存储账号密码和基础信息角色表sys_role存储角色名称和描述菜单表sys_menu存储系统菜单和按钮权限用户角色表和角色菜单表分别存关联关系。菜单表我建议做成父子结构通过parent_id字段自关联这样页面导航就可以用一个递归方法渲染出多级菜单。权限控制的思路是登录成功时查询当前用户的角色再查出所有关联的菜单权限码把权限码列表放到Session里。之后每个Action执行前拦截器检查当前用户是否拥有对应权限码没有就直接跳转到403页面。数据库建表脚本可以直接放到项目根目录的sql/文件夹下并附上初始化的测试数据。答辩时评委基本都会让现场演示一个有测试数据的系统演示起来远比一个空表系统有说服力。4. JSP页面层与Struts2协作渲染、传参与跳转细节4.1 用S标签和EL表达式替换页面里的Java片段SSH老项目的JSP页面最容易被批评的问题就是嵌入了大量% %Java脚本片段。一个典型的反面案例是客户列表页里用for循环加out.println去渲染表格。这种写法在课程设计里可能能跑但代码可读性极差而且混入的Java代码多了页面维护成本会直线上升。正确做法是用Struts2标签库前缀通常为s加EL表达式。客户列表页list.jsp的核心部分应该是s:iterator valuepageResult.list varcustomer statusst tr tds:property value#customer.name//td tds:property value#customer.level//td tds:property value#customer.phone//td td a href${pageContext.request.contextPath}/customer_edit.action?id${customer.id}编辑/a a href${pageContext.request.contextPath}/customer_delete.action?id${customer.id} onclickreturn confirm(确认删除)删除/a /td /tr /s:iterator这样页面里没有一行Java代码标签的语义也清楚。如果项目里用了JSTL还可以配合c标签做分页条。注意使用s:property输出属性时value里必须是OGNL表达式使用EL表达式的地方则不需要前缀。两种表达式混着用经常把人绕晕我建议统一用OGNL学习成本虽然高一点但是和Struts2集成得最自然。4.2 Action参数接收的三种方式与注意事项Struts2接收页面参数有好几种方式最常用的是Action中定义同名属性。比如表单里有一个customer.name输入框Action里就定义一个Customer customer属性并提供getter和setterStruts2会自动把页面参数填充进对象的name字段。另一种方式是实现ModelDrivenCustomer接口getModel()返回一个Customer实例Struts2会把所有同名参数直接注入这个model对象。这种方式的访问最省事页面里直接写name、phone不需要customer.name前缀。但它的缺点也很明显一个Action只能有一个model当一个页面同时提交客户和联系人两类数据时就会很别扭。还有一种更灵活的方式是使用Param注解的方式或者在Action里直接操作ActionContext.getContext().getParameters()但普通业务场景用到的机会不多。这里有一个常见的坑页面提交的日期字符串形如2025-05-20而实体属性是java.util.Date类型Struts2默认不能自动完成转换必须提供类型转换器或者在实体属性上配合Struts2的日期格式化注解。课设阶段最简单的做法是日期属性都用String接Service层保存时再转成Date虽然丑但绝对不报错。4.3 登录拦截器和权限过滤的通用写法登录校验是每个web项目都绕不开的。Struts2实现登录拦截的标准方式是写一个拦截器类继承AbstractInterceptor在intercept()方法里判断Session中是否存在登录用户。public class LoginInterceptor extends AbstractInterceptor { Override public String intercept(ActionInvocation invocation) throws Exception { MapString, Object session invocation.getInvocationContext().getSession(); User user (User) session.get(loginUser); if (user null) { return login; } return invocation.invoke(); } }然后在struts.xml里配置拦截器栈把LoginInterceptor加到默认栈中并且用excludeMethods把loginAction的执行方法排除掉比如user_login不能拦截。如果不排除登录功能本身也会被拦截形成一个死循环。权限过滤的思路类似先判断登录用户是否为空再判断当前用户角色是否具备访问当前Action的权限码。可以把权限码集合放在Session里用Map结构缓存登录后只查一次避免每次请求都查数据库。5. 部署与排错从导入源码到跑通全流程5.1 Eclipse/IDEA导入老项目的正确姿势SSH项目大多是用Eclipse建立的目录结构通常是src、WebContent、config等。如果你用的是IDEA导入时要注意几个点。第一不要把整个zip直接解压后用IDEA打开正确流程是File - New - Project from Existing Sources然后选择项目目录IDEA会自动识别Web项目结构。第二SSH项目依赖的jar包通常会放在WebContent/WEB-INF/lib目录下导入后要确认这些jar已经被识别到否则编译会报大量红色错误。第三检查Project Structure里的Artifacts设置确保Web应用名称正确Facet里识别到了Web模块否则部署到Tomcat时会404。如果你在设置JSP页面高亮和代码提示时发现没有语法提示多半是IDEA的Facet里没有正确绑定Web根目录。在Project Structure - Facets - Web里把Web Resource Directory指向WebContent目录再把Web.xml路径指定一下JSP的语法高亮、EL表达式提示就全出来了。5.2 数据库初始化与连接参数的坑数据库初始化基本是照葫芦画瓢先用Navicat或命令行创建数据库然后执行项目里的sql脚本。需要特别注意的是字符集。如果你的表结构里大量使用varchar建议建库语句用CREATE DATABASE crm DEFAULT CHARACTER SET utf8否则中文会保存成问号。然后就是修改数据库连接参数位置在applicationContext.xml里的dataSource配置property namejdbcUrl valuejdbc:mysql://localhost:3306/crm?useUnicodetrueamp;characterEncodingutf8amp;useSSLfalseamp;serverTimezoneAsia/Shanghai/ property namedriverClass valuecom.mysql.jdbc.Driver/ property nameuser valueroot/ property namepassword value123456/如果你是MySQL 8.x版本驱动类名要改成com.mysql.cj.jdbc.Driver并且URL里必须带serverTimezone参数否则会报时区错误。如果项目里的jar包还是旧的5.x驱动建议直接去Maven仓库下最新的5.1.49版替换掉最老的版本useSSLfalse也要加上避免SSL握手警告打扰你排查其他问题。5.3 常见报错清单与解决方案我梳理一下这类SSH项目最容易出现的几个典型报错404页面先看URL有没有拼对再看struts.xml里action的namespace是否正确最后确认Artifacts里是否已经包含struts.xml和所有的class文件。很多404是部署目录里没更新clean一下Tomcat再重新发布就能解决。500错误显示ClassNotFound通常是jar包没有进入WEB-INF/lib目录也就是没有被构建到Artifacts里。在IDEA的Artifacts界面的Available Elements里把jar包右键选择Put into WEB-INF/lib。Hibernate的MappingException说的是某个实体映射文件找不到检查hbm.xml文件位置是否在classpath下以及实体类的配置里类的完整限定名是否对。中文乱码在web.xml里配置CharacterEncodingFilter强制请求和响应都用UTF-8数据库连接URL加characterEncodingutf8JSP页面里保证pageEncodingUTF-8。这三个位置缺一不可。com.mysql.jdbc.exceptions.jdbc4.CommunicationsException距离上一次通信时间超过连接池的空闲时间连接被数据库关闭但连接池还拿着这个死连接。解决办法是给C3P0配置testConnectionOnCheckout或者定期用testPeriod检测连接存活。6. 拿到这套源码之后代码复盘与升级路线6.1 源码结构里最容易忽略的问题很多源码包下载下来里面文档和代码好像很完整但真正导入开发工具后总会少这少那。我复盘这个项目时重点看了三个地方。第一是配置文件之间的引用路径。SSH项目的配置文件分布在src和config目录下Spring配置文件用classpath加载Struts的配置用package里的namespace做路由路径一错框架启动就会失败。第二是实体类与数据表的字段映射注意看hbm.xml里每个字段的column属性和数据库表是否一致有任何一个字段对不上查询就会报SQL异常。第三是文档的版本描述看文档里写的环境是JDK 1.7还是1.8Tomcat是7还是8MySQL是5.6还是8.0。老项目文档和实际环境不一致太常见了按文档操作报错之后要优先怀疑版本问题。如果你拿到源码后想快速跑起来我建议的顺序是先建库执行SQL脚本再改数据库连接配置然后导入项目到IDE最后启动Tomcat。不要先启动项目再改配置那样报错信息会很多不好定位。6.2 从SSH到SSM再到Spring Boot的演进思路这个CRM项目如果作为毕业设计答辩时老师大概率会问一句你觉得这套架构有哪些不足怎么改进。你要能接住这个问题。SSH最明显的问题是配置繁杂Struts2的拦截器机制和Servlet规范差异较大Hibernate在复杂查询上的灵活性和MyBatis相比有差距。演进路线很清晰用Spring MVC替代Struts2、用MyBatis替代Hibernate组成SSM再进一步就是用Spring Boot的自动配置把XML配置全部干掉整合成Spring Boot MyBatis Plus Vue的前后端分离架构。我建议在文档里专门写一节未来改进方向把业务层面和技术层面分开说。业务层面可以提数据看板、移动端适配、客户公海池自动分配技术层面可以提代码生成器、Redis缓存热点客户、RabbitMQ处理消息通知。这样既展示了思考深度也给答辩留了充足的提问空间。6.3 文档应该怎么写才能加分课程设计或毕业设计的文档我见过太多流水账第一章项目背景第二章需求分析第三章数据库设计第四章代码实现第五章测试——格式挑不出毛病但内容全是空话。真正能加分的文档写法是写清为什么这么设计而不是只写设计了什么。比如数据库设计章节除了贴表结构要补充一段客户和联系人为什么是一对多而不是多对多跟进记录为什么采用追加式而不是覆盖式。技术选型章节要写一下为什么选择SSH而不是SSM这个项目里SSH的哪些特性被用到了、哪些坑是怎么避开的。我在补文档的时候把测试章节从系统功能测试细化成了功能测试、权限测试、并发场景测试三块并且给每块配了具体的测试用例和预期结果整个文档的含金量立刻不一样。如果时间充裕还可以在文档里加部署说明把环境要求、数据库初始化步骤、Tomcat配置、常见问题一一列清楚。很多评分老师会照着文档尝试部署如果你的文档能让他们一次性跑通第一印象分就稳了——我最开始拿到这个项目时文档里没有写MySQL 8的驱动替换说明自己折腾了半小时才跑通后来补文档时把这块写进去了这就是实用文档和凑字数文档的区别。最后再分享一点个人体会。虽然SSH这套技术栈在真实开发中已经逐渐被Spring Boot生态取代但作为理解Java Web分层架构的教科书它依然有不可替代的价值。我在帮人梳理这个CRM项目时最大的感受是框架选型只是表面真正决定项目质量的永远是分层的清晰度、表设计的合理性和异常处理的完备性。如果你能把这个SSH版CRM做到每一个请求到数据库的链路都能讲清楚、每一张表的功能边界都说得明白那么过渡到Spring Boot和微服务只是时间问题反之即使换成了最新的技术栈写出来的代码也还是那锅粥。按照上面这条路线去复盘和改造一定比直接下个Spring Boot版本的代码改成自己名字要扎实得多。本文还有配套的精品资源点击获取