CRM系统即客户关系管理系统,是指企业用CRM技术来管理与客户之间的关系。他的目标是缩减销售周期和销售成本,增加收入,寻找扩展业务所需的新的市场和渠道以及提高客户的价值,满意度,营利性和忠实度。CRM项目的实施可以分为3步,即应用业务集成。业务诗句分析和决策执行。
2.CRM开发环境和技术 <1> 项目业务介绍客户关系管理是指企业为提高核心竞争力,利用相应的技术信息以及互联网技术协调企业与顾客间在消费,营销和服务上的交互,从而提升其管理方式,向客户提供创新式的个性化的客户交互和服务的过程,其最终目标是吸引新客户,保留老客户以及将已有客户转为忠实客户,增加市场
<2>开发环境项目名称:CRM客户管理系统
系统作用:公司客户关系管理,潜在客户开发及订单合同管理
开发环境:IDEA Windows10 jdk1.8 Maven Mysql8
需要的工具:postman fiddler抓包工具或浏览器开发者工具
包含系统基本的用户登录,退出,记住我,密码修改等基本操作。
2. 营销管理营销机会管理 :企业客户的质询需求所建立的信息录入功能,方便销售人员进行后续的客户需求跟
踪。
客户信息管理 :Crm 系统中完整记录客户信息来源的数据、企业与客户交往、客户订单查询等信息录
入功能,方便企业与客户进行相应的信息交流与后续合作。
客户流失管理 :Crm 通过一定规则机制所定义的流失客户(无效客户),通过该规则可以有效管理客
户信息资源,提高营销开发的效率。
服务管理是针对客户而开发的功能,针对客户要求,Crm 提供客户相应的信息质询,反馈与投诉功
能,提高企业对于客户的服务质量。
营销开发计划 :开发计划是根据营销机会而来,对于企业质询的客户,会有相应的销售人员对于该客户进行具 体的沟通交流,此时对于整个 Crm 系统而言,通过营销开发计划来进行相应的信息管理,提高客户的购买企 业产品的可能性。
Crm 提供的数据报表功能能够帮助企业了解客户整体分布,了解客户开发结果整体信息,从而帮助企
业整体调整客户开发计划,提高企业的在市场中的竞争力度。
系统管理包含常量字典维护工作,以及权限管理模块,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加解密等
实现思路
2.全局统一的异常处理及非法请求的拦截 <1> 统一的异常处理
全局异常实现思路:
控制层的方法返回的内容两种情况
- 视图:视图异常Json:方法执行错误 返回错误json信息
全局异常拦截器的实现,简化了try-catch代码
实现 HandlerExceptionResolver 接口 ,处理应用程序异常信息
对于后端菜单资源,这里要求用户必须进行登录来保护 web 资源的安全性,此时引入非法请求拦截功
能。
实现思路:
判断用户是否是登录状态
获取cookie对象,解析用户ID的值
如果用户ID不为空,且在数据库中存在对应的用户记录,表示请求合法
否则,请求不合法,进行拦截,重定向到登录页面
定义拦截器:在新建 interceptors 包,创建 NoLoginInterceptor 类,并继承 HandlerInterceptorAdapter 适配器,
实现拦截器功能。
记住我功能核心在于当用户上次登录时如果点击了记住我,下次在重新打开浏览器时可以不用选择登
录,此时可以借助拦截器 + cookie 来实现,当用户在登录时,如果用户点击了记住我功能,默认设置
cookie存储时间为7天即可。
实现思路
5.退出功能
实现思路
找到 “退出登录” 的元素,并绑定点击事件。当用户点击退出时,清空cookie信息
在 main.js 中,通过类选择器绑定元素的点击事件
$(".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.营销机会数据添加
实现思路
效果图
效果图
实现思路
效果图
效果图
基本概念:RBAC是基于角色的访问控制( Role-based Access Control )在RBAC中,权限与角色相关联,用户
通过扮演适当的角色从而得到这些角色的权限。这样管理都是层级相互依赖的,权限赋予给角色,角色
又赋予用户,这样的权限设计很清楚,管理起来很方便。
核心思想
效果图
UserController.java
@RequestMapping("user/save")
@ResponseBody
public ResultInfo saveUser(User user) {
userService.saveUser(user);
return success("用户添加成功!");
}
UserService.java
@Transactional(propagation = Propagation.REQUIRED)
public void saveUser(User user) {
// 1. 参数校验
checkParams(user.getUserName(), user.getEmail(), user.getPhone());
// 2. 设置默认参数
user.setIsValid(1);
user.setCreateDate(new Date());
user.setUpdateDate(new Date());
user.setUserPwd(Md5Util.encode("123456"));
// 3. 执行添加,判断结果
AssertUtil.isTrue(userMapper.insertSelective(user) == null, "用户添加失败!");
}
private void checkParams(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), "手机号码格式不正确!");
}
用户更新
核心思想
效果图
效果图
效果图
当完成角色权限添加功能后,下一步就是对角色操作的资源进行认证操作,这里对于认证包含两块:
菜单级别显示控制后端方法访问控制
查询出改用户所拥有的角色,然后根据角色查询出拥有的权限码,具体实现如下:
@RequestMapping("main")
public String main(HttpServletRequest request) {
//通过获取cookie用户ID
Integer userId = LoginUserUtil.releaseUserIdFromcookie(request);
//查询用户对象,设置session作用域
User user = userService.selectByPrimaryKey(userId);
request.getSession().setAttribute("user", user);
//通过当前登录用户ID,查询当前登录用户拥有的资源列表(查询对应的资源授权码)
List permissions = null;
permissions = permissionService.queryUserHasRoleHasPermissionByUserId(userId);
//将集合设置作用域中(Session作用域)
request.getSession().setAttribute("permissions", permissions);
return "main";
}
菜单级别显示控制
系统根据登录用户扮演的不同角色来对登录用户操作的菜单进行动态控制显示操作,这里显示的控制使用freemarker指令+内建函数实现
前端代码根据权限码控制前台
登录scott用户
效果图
实现了菜单级别显示控制,但最终客户端有可能会通过浏览器来输入资源地址从而越过ui界面来访问后
端资源,所以接下来加入控制方法级别资源的访问控制操作,这里使用aop+自定义注解实现
自定义注解类:表示资源所需的权限码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface RequiredPermission {
//权限码
String code() default "";
}
定义aop切面类 拦截指定注解标注的方法:
@Component
@Aspect
public class PermissionProxy {
@Resource
private HttpSession session;
@Around(value = "@annotation(org.example.crm.annotation.RequiredPermission)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object result = null;
// 得到当前登录用户拥有的权限 (session作用域)
List permissions = (List) session.getAttribute("permissions");
// 判断用户是否拥有权限
if (null == permissions || permissions.size() < 1) {
// 抛出认证异常
throw new AuthException();
}
// 得到对应的目标
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
// 得到方法上的注解
RequiredPermission requiredPermission = methodSignature.getMethod().getDeclaredAnnotation(RequiredPermission.class);
// 判断注解上对应的状态码
if (!(permissions.contains(requiredPermission.code()))) {
// 如果权限中不包含当前方法上注解指定的权限码,则抛出异常
throw new AuthException();
}
result = pjp.proceed();
return result;
}
}



