一、项目的简单描绘
1.什么是CRM
CRM系统即客户关系管理系统,是指企业用CRM技术来管理与客户之间的关系。他的目的是缩减销售周期和销售本钱,增加收入,寻找扩展业务所需的新的市场和渠道以及进步客户的价值,满意度,营利性和忠实度。CRM项目的施行可以分为3步,即应用业务集成。业务诗句分析和决策执行。
2.CRM开发环境和技术
<1> 项目业务介绍
客户关系管理是指企业为进步核心竞争力,利用相应的技术信息以及互联网技术协调企业与顾客间在消费,营销和效劳上的交互,从而提升其管理方式,向客户提供创新式的个性化的客户交互和效劳的过程,其最终目的是吸引新客户,保留老客户以及将已有客户转为忠实客户,增加市场
<2>开发环境
项目名称:CRM客户管理系统
系统作用:公司客户关系管理,潜在客户开发及订单合同管理
开发环境:IDEA Windows10 jdk1.8 Maven Mysql8
需要的工具:postman fiddler抓包工具或阅读器开发者工具
二、项目模块分析
1. 根底模块
包含系统根本的用户登录,退出,记住我,密码修改等根本操作。
2. 营销管理
营销时机管理 :企业客户的质询需求所建立的信息录入功能,方便销售人员停止后续的客户需求跟
踪。
3. 客户管理
客户信息管理 :Crm 系统中完好记录客户信息来源的数据、企业与客户交往、客户订单查询等信息录
入功能,方便企业与客户停止相应的信息交流与后续合作。
客户流失管理 :Crm 通过一定规则机制所定义的流失客户(无效客户),通过该规则可以有效管理客
户信息资源,进步营销开发的效率。
4. 效劳管理
效劳管理是针对客户而开发的功能,针对客户要求,Crm 提供客户相应的信息质询,反响与投诉功
能,进步企业对于客户的效劳质量。
营销开发方案 :开发方案是根据营销时机而来,对于企业质询的客户,会有相应的销售人员对于该客户停止具 体的沟通交流,此时对于整个 Crm 系统而言,通过营销开发方案来停止相应的信息管理,进步客户的购置企 业产品的可能性。
5. 数据报表
Crm 提供的数据报表功能可以协助企业理解客户整体分布,理解客户开发结果整体信息,从而协助企
业整体调整客户开发方案,进步企业的在市场中的竞争力度。
6. 系统管理
系统管理包含常量字典维护工作,以及权限管理模块,Crm 权限管理是基于角色的一种权限控制,基
于 RBAC 实现基于角色的权限控制,通过不同角色的用户登录该系统后展示系统不同的操作功能,从而
到达对不同角色完成不同操作功能。
三、环境搭建
1.创建SpringBoot项目,导入依赖(见源代码)
2.在src/main/resources 目录下新建 application.yml 配置文件
3.新建 org.example.crm.controller 包,添加系统登录,主页面转发代码 。
4.添加静态资源:在 src/main/resources 目录下新建 public 目录,寄存系统相关静态资源文件,拷贝静态文件内容到
public 目录。
5.添加视图模板:在 src/main/resources 目录下新建 views 目录,添加 index.ftl、main.ftl 等文件。 (详细视图文件详见
相关目录)
6.添加启动类:在 org.example.crm 包下新建 Starter.java 。
7.添加Base包:主要用户对Controller,Service Dao层的统一控制,BaseQuery用于控制按条件搜索的对象,ResultInfo是后端返回的对象的统一封装
8.准备MyBatis代码统一生成工具(generatorConfig.xml)
9.导入工具类,主要有:根据Cookie获取作用域,登录胜利返回userIdStr加密,Md5协议加密,判断电话号码的格式,userID加解密等
四、项目的正式开发
用户管理
1.用户登录
实现思路- /**
- * 用户登录
- * 1. 验证参数
- * 姓名 非空判断
- * 密码 非空判断
- * 2. 根据用户名,查询用户对象
- * 3. 判断用户是否存在
- * 用户对象为空,记录不存在,方法完毕
- * 4. 用户对象不为空
- * 用户存在,校验密码
- * 密码不正确,方法完毕
- * 5. 密码正确
- * 用户登录胜利,返回用户的相关信息 (定义UserModel类,返回用户某些信息)
- */
复制代码 2.全局统一的异常处置及非法恳求的拦截
<1> 统一的异常处置
全局异常实现思路:
控制层的方法返回的内容两种情况
视图:视图异常Json:方法执行错误 返回错误json信息
全局异常拦截器的实现,简化了try-catch代码
实现 HandlerExceptionResolver 接口 ,处置应用程序异常信息
<2>非法恳求拦截
对于后端菜单资源,这里要求用户必需停止登录来维护 web 资源的安全性,此时引入非法恳求拦截功
能。
实现思路:
判断用户是否是登录状态
获取Cookie对象,解析用户ID的值
假设用户ID不为空,且在数据库中存在对应的用户记录,表示恳求合法
否则,恳求不合法,停止拦截,重定向到登录页面
定义拦截器:在新建 interceptors 包,创建 NoLoginInterceptor 类,并继承 HandlerInterceptorAdapter 适配器,
实现拦截器功能。
3.记住我功能
记住我功能核心在于当用户上次登录时假设点击了记住我,下次在重新翻开阅读器时可以不用选择登
录,此时可以借助拦截器 + cookie 来实现,当用户在登录时,假设用户点击了记住我功能,默认设置
cookie存储时间为7天即可。
4.密码修改功能
实现思路- /**
- * 用户密码修改
- * 1. 参数校验
- * userId 非空 用户对象必需存在
- * oldPassword 非空 与数据库中密文密码坚持一致
- * newPassword 非空 与原始密码不能相同
- * confirmPassword 非空 与新密码坚持一致
- * 2. 设置用户新密码
- * 新密码停止加密处置
- * 3. 执行更新操作
- * 受影响的行数小于1,则表示修改失败
- */
复制代码 5.退出功能
实现思路
找到 “退出登录” 的元素,并绑定点击事件。当用户点击退出时,清空cookie信息
在 main.js 中,通过类选择器绑定元素的点击事件- /**
- * 用户退出
- * 删除cookie
- */
- $(".login-out").click(function () {
- // 删除 cookie
- $.removeCookie("userIdStr", {domain:"localhost",path:"/crm"});
- $.removeCookie("userName", {domain:"localhost",path:"/crm"});
- $.removeCookie("trueName", {domain:"localhost",path:"/crm"});
- // 跳转到登录页面 (父窗口跳转)
- window.parent.location.href = ctx + "/index";
- });
复制代码 营销时机管理功能实现(CRUD操作)
1.营销时机数据添加
实现思路- /**
- * 营销时机数据添加
- * 1.参数校验
- * customerName:非空
- * linkMan:非空
- * linkPhone:非空 11位手机号
- * 2.设置相关参数默认值
- * state:默认未分配 假设选择分配人 state 为已分配
- * assignTime:假设 假设选择分配人 时间为当前系统时间
- * devResult:默认未开发 假设选择分配人devResult为开发中 0-未开发 1-开发中 2-开发胜利
- 3-开发失败
- * isValid:默认有效数据(1-有效 0-无效)
- * createDate updateDate:默认当前系统时间
- * 3.执行添加 判断结果
- */
复制代码 效果图
2.营销时机数据查询
效果图
3.营销时机数据更新
实现思路- /**
- * 营销时机数据更新
- * 1.参数校验
- * id:记录必需存在
- * customerName:非空
- * linkMan:非空
- * linkPhone:非空,11位手机号
- * 2. 设置相关参数值
- 3.4.1.2. 核心代码
- SaleChanceService.java
- * updateDate:系统当前时间
- * 原始记录 未分配 修改后改为已分配(由分配人决定)
- * state 0->1
- * assginTime 系统当前时间
- * devResult 0-->1
- * 原始记录 已分配 修改后 为未分配
- * state 1-->0
- * assignTime 待定 null
- * devResult 1-->0
- * 3.执行更新 判断结果
- */
复制代码 效果图
4. 营销时机数据删除
效果图
CRM - 权限管理
根本概念:RBAC是基于角色的访问控制( Role-Based Access Control )在RBAC中,权限与角色相关联,用户
通过扮演适当的角色从而得到这些角色的权限。这样管理都是层级互相依赖的,权限赋予给角色,角色
又赋予用户,这样的权限设计很清楚,管理起来很方便。
1.用户管理功能
用户添加
核心思想- /**
- * 添加用户
- * 1. 参数校验
- * 用户名 非空 唯一性
- * 邮箱 非空
- * 手机号 非空 格式合法
- * 2. 设置默认参数
- * isValid 1
- * creteDate 当前时间
- * updateDate 当前时间
- * userPwd 123456 -> md5加密
- * 3. 执行添加,判断结果
- */
复制代码 效果图
- /**
- * 添加用户
- * @param user
- * @return
- */@RequestMapping("user/save")@ResponseBodypublicResultInfosaveUser(User user){
- userService.saveUser(user);returnsuccess("用户添加胜利!");}
复制代码- /**
- * 添加用户
- * @param user
- */@Transactional(propagation =Propagation.REQUIRED)publicvoidsaveUser(User user){// 1. 参数校验checkParams(user.getUserName(), user.getEmail(), user.getPhone());// 2. 设置默认参数
- user.setIsValid(1);
- user.setCreateDate(newDate());
- user.setUpdateDate(newDate());
- user.setUserPwd(Md5Util.encode("123456"));// 3. 执行添加,判断结果AssertUtil.isTrue(userMapper.insertSelective(user)==null,"用户添加失败!");}/**
- * 参数校验
- * @param userName
- * @param email
- * @param phone
- */privatevoidcheckParams(String userName,String email,String phone){AssertUtil.isTrue(StringUtils.isBlank(userName),"用户名不能为空!");// 验证用户名是否存在User temp = userMapper.queryUserByUserName(userName);AssertUtil.isTrue(null!= temp,"该用户已存在!");AssertUtil.isTrue(StringUtils.isBlank(email),"请输入邮箱地址!");AssertUtil.isTrue(!PhoneUtil.isMobile(phone),"手机号码格式不正确!");}
复制代码 用户更新
核心思想- /**
- * 更新用户
- * 1. 参数校验
- * id 非空 记录必需存在
- * 用户名 非空 唯一性
- * email 非空
- * 手机号 非空 格式合法
- * 2. 设置默认参数
- * updateDate
- * 3. 执行更新,判断结果
- * @param user
- */
复制代码 效果图
用户删除
效果图
用户角色关联
效果图
2.角色权限功能
当完成角色权限添加功能后,下一步就是对角色操作的资源停止认证操作,这里对于认证包含两块:
查询出改用户所拥有的角色,然后根据角色查询出拥有的权限码,详细实现如下:- @RequestMapping("main")publicStringmain(HttpServletRequest request){//通过获取cookie用户IDInteger userId =LoginUserUtil.releaseUserIdFromCookie(request);//查询用户对象,设置session作用域User user = userService.selectByPrimaryKey(userId);
- request.getSession().setAttribute("user", user);//通过当前登录用户ID,查询当前登录用户拥有的资源列表(查询对应的资源受权码)List<String> permissions =null;
- permissions = permissionService.queryUserHasRoleHasPermissionByUserId(userId);//将集合设置作用域中(Session作用域)
- request.getSession().setAttribute("permissions", permissions);return"main";}
复制代码 菜单级别显示控制
系统根据登录用户扮演的不同角色来对登录用户操作的菜单停止动态控制显示操作,这里显示的控制使用freemarker指令+内建函数实现
前端代码根据权限码控制前台
登录scott用户
效果图
后端方法级别访问控制(AOP+注解实现)
实现了菜单级别显示控制,但最终客户端有可能会通过阅读器来输入资源地址从而越过ui界面来访问后
端资源,所以接下来参与控制方法级别资源的访问控制操作,这里使用aop+自定义注解实现
自定义注解类:表示资源所需的权限码- @Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented/**
- * 定义方法需要的对应资源的权限码
- */public@interfaceRequiredPermission{//权限码Stringcode()default"";}
复制代码 定义aop切面类 拦截指定注解标注的方法:- @Component@AspectpublicclassPermissionProxy{@ResourceprivateHttpSession session;/**
- * 切面会拦截指定包下的指定注解
- * 拦截com.xxxx.crm.annoation的RequiredPermission注解
- *
- * @param pjp
- * @return java.lang.Object
- */@Around(value ="@annotation(org.example.crm.annotation.RequiredPermission)")publicObjectaround(ProceedingJoinPoint pjp)throwsThrowable{Object result =null;// 得到当前登录用户拥有的权限 (session作用域)List<String> permissions =(List<String>) session.getAttribute("permissions");// 判断用户是否拥有权限if(null== permissions || permissions.size()<1){// 抛出认证异常thrownewAuthException();}// 得到对应的目的MethodSignature methodSignature =(MethodSignature) pjp.getSignature();// 得到方法上的注解RequiredPermission requiredPermission = methodSignature.getMethod().getDeclaredAnnotation(RequiredPermission.class);// 判断注解上对应的状态码if(!(permissions.contains(requiredPermission.code()))){// 假设权限中不包含当前方法上注解指定的权限码,则抛出异常thrownewAuthException();}
- result = pjp.proceed();return result;}}
复制代码 |
|