二了吧唧
楼主
发布于 2023-4-21 15:33:38
阅读 2689
查看全部
目录
CRM客户管理系统一.对CRM的项目的简单描绘
二.项目准备及模块分析
三.项目的正式开发
1.用户管理模块
<1>.表构造分析<2>.用户登录<3>全局统一的异常处置及非法恳求的拦截
<3>记住我功能
2.营销管理模块(CRUD操作,见源码)
3.权限管理模块(CRUD操作见源码)
<1>.模块功能及表的构造设计<2>角色权限功能
(1).菜单级别显示控制(2).后端方法级别访问控制(AOP+注解实现)
4.客户管理模块(CRUD操作见源码)
5.效劳管理(CRUD操作见源码)
6.统计报表管理(CRUD见源码)
<1>.功能实现<2>.使用ECharts对数据停止分析
欢送Star [CRM客户管理系统](https://gitee.com/zhuyunfei20010415/crm)
CRM客户管理系统
自己负责后端的开发(Java)
一.对CRM的项目的简单描绘
1.什么是CRM
CRM系统即客户关系管理系统,是指企业用CRM技术来管理与客户之间的关系。他的目的是缩减销售周期和销售本钱,增加收入,寻找扩展业务所需的新的市场和渠道以及进步客户的价值,满意度,营利性和忠实度。CRM项目的施行可以分为3步,即应用业务集成。业务诗句分析和决策执行。
2.CRM开发环境和技术
<1> 项目业务介绍
客户关系管理是指企业为进步核心竞争力,利用相应的技术信息以及互联网技术协调企业与顾客间在消费,营销和效劳上的交互,从而提升其管理方式,向客户提供创新式的个性化的客户交互和效劳的过程,其最终目的是吸引新客户,保留老客户以及将已有客户转为忠实客户,增加市场
<2>开发环境
项目名称:CRM客户管理系统
系统作用:公司客户关系管理,潜在客户开发及订单合同管理
开发环境:IDEA Windows10 jdk1.8 Maven Mysql8
需要的工具:postman fiddler抓包工具或阅读器开发者工具
<3>开发技术
前端:LayUI freeMaker
后端:Spring SpringMVC SpringBoot MyBatis Maven MySQL8 Linux CentOS ECharts(折线和饼状图)权限管理 定时任务调度(quartz)CentOS Lombok
二.项目准备及模块分析
1.模块分析总览
1.根底模块:包含系统根本的用户登录,退出,记住我,密码修改等根本操作。
2.营销管理:
营销时机管理:企业客户的质询需求所建立的信息录入功能
客户开发方案:开发方案是根据营销时机而来,对于企业质询的客户,会有相应的销售人员对于该客户
停止详细的沟通交流,此时对于整个 Crm 系统而言,通过营销开发方案来停止相应的信息管理,进步
客户的购置企业产品的可能性。
3.客户管理:
客户信息管理 :Crm 系统中完好记录客户信息来源的数据、企业与客户交往、客户订单查询等信息录
入功能,方便企业与客户停止相应的信息交流与后续合作。
客户流失管理 :Crm 通过一定规则机制所定义的流失客户(无效客户),通过该规则可以有效管理客
户信息资源,进步营销开发的效率。
4.效劳管理:效劳管理是针对客户而开发的功能,针对客户要求,Crm 提供客户相应的信息质询,反响与投诉功能,
进步企业对于客户的效劳质量。
5.数据报表:
Crm 提供的数据报表功能可以协助企业理解客户整体分布,理解客户开发结果整体信息,从而协助企业
整体调整客户开发方案,进步企业的在市场中的竞争力度。
6.系统管理:系统管理包含常量字典维护工作,以及权限管理模块,Crm 权限管理是基于角色的一种权限控制,基于
RBAC 实现基于角色的权限控制,通过不同角色的用户登录该系统后展示系统不同的操作功能,从而达
到对不同角色完成不同操作功能。
2.项目前期环境的搭建
1.创建SpringBoot项目,导入依赖(见源代码)
2.在src/main/resources 目录下新建 application.yml 配置文件- ## 端口号 上下文途径
- server:
- port:8080
- servlet:
- context-path:/crm
- ## 数据源配置
- spring:
- datasource:
- type:com.mchange.v2.c3p0.ComboPooledDataSource
- driver-class-name:com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://127.0.0.1:3306/crm?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
- username:
- password:
- ## freemarker
- freemarker:
- suffix:.ftl
- content-type: text/html
- charset: UTF-8
- template-loader-path: classpath:/views/
- ## 启用热部署
- devtools:
- restart:
- enabled:true
- additional-paths: src/main/java
- ## mybatis 配置
- mybatis:
- mapper-locations: classpath:/mappers/*.xml
- type-aliases-package: org.example.crm.vo;org.example.crm.query;org.example.crm.dto
- configuration:
- map-underscore-to-camel-case: true
- ## pageHelper 分页
- pagehelper:
- helper-dialect: mysql
- ## 设置 dao 日志打印级别
- logging:
- level:
- org:
- example:
- crm:
- dao: debug
复制代码 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 ,添加启动项目相关代码如下:- packageorg.example.crm;importorg.mybatis.spring.annotation.MapperScan;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication@MapperScan("org.example.crm.dao")//启用定时任务@EnableSchedulingpublicclassStarter{publicstaticvoidmain(String[] args){SpringApplication.run(Starter.class);}}
复制代码 7.添加Base包:主要用户对Controller,Service Dao层的统一控制,BaseQuery用于控制按条件搜索的对象,ResultInfo是后端返回的对象的统一封装
项目搭建构造:
8.准备MyBatis代码统一生成工具(generatorConfig.xml)
这里注意:工具有点缺陷,每次需要改工具作用的表名
使用mybatis-generator生成Mybatis代码。可以生成 vo 类、能生成 mapper 映射文件(其中包括根本
的增删改查功能)、能生成 mapper 接口。
命令: mybatis-generator:generate -e- <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration
- PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><!--
- 数据库驱动
- 在左侧project边栏的ExternalLibraries中找到mysql的驱动,右键选择copy path
- --><classPathEntry location="D:\Repository\Maven\mysql\mysql-connector-java\5.1.49\mysql-connector-java-5.1.49.jar"/><context id="DB2Tables" targetRuntime="MyBatis3"><commentGenerator><!-- 是否去除日期那行注释 --><property name="suppressDate" value="false"/><!-- 是否去除自动生成的注释 true:是 : false:否 --><property name="suppressAllComments" value="false"/></commentGenerator><!-- 数据库链接地址账号密码 --><jdbcConnection
- driverClass="com.mysql.jdbc.Driver"
- connectionURL="jdbc:mysql://localhost:3306/crm?serverTimezone=GMT%2B8"
- userId="root"
- password="sn20000904"></jdbcConnection><!--
- java类型处置器
- 用于处置DB中的类型到Java中的类型,默认使用JavaTypeResolverDefaultImpl;
- 注意一点,默认会先尝试使用Integer,Long,Short等来对应DECIMAL和NUMERIC数据类型;
- true:使用 BigDecimal对应DECIMAL和NUMERIC数据类型
- false:默认,把JDBC DECIMAL和NUMERIC类型解析为Integer--><javaTypeResolver><property name="forceBigDecimals" value="false"/></javaTypeResolver><!-- 生成Model类寄存位置 --><javaModelGenerator targetPackage="org.example.crm.model" targetProject="src/main/java"><!-- 在targetPackage的根底上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false--><property name="enableSubPackages" value="true"/><!-- 设置是否在getter方法中,对String类型字段调用trim()方法 --><property name="trimStrings" value="true"/></javaModelGenerator><!--生成映射文件寄存位置--><sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources"><property name="enableSubPackages" value="true"/></sqlMapGenerator><!--生成Dao类寄存位置--><javaClientGenerator type="XMLMAPPER" targetPackage="org.example.crm.dao" targetProject="src/main/java"><property name="enableSubPackages" value="true"/></javaClientGenerator><!--改表名 --><table tableName="t_customer_serve" domainObjectName="CustomerServe"
- enableCountByExample="false" enableUpdateByExample="false"
- enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table></context></generatorConfiguration>
复制代码 9.导入工具类,主要有:根据Cookie获取作用域,登录胜利返回userIdStr加密,Md5协议加密,判断电话号码的格式,userID加解密等
三.项目的正式开发
主要讲解核心模块的核心代码
1.用户管理模块
<1>.表构造分析
<2>.用户登录
定义UserModel类,用于用户登录胜利返回的用户信息,用来设置前端的Cookie- @Getter@SetterpublicclassUserVo{//private Integer userId;//寄存在前端cookie中加密后的IdprivateString userIdStr;privateString userName;privateString trueName;}
复制代码 设置cookie- layer.msg("登录胜利!",function(){// 判断用户是否选择记住密码(判断复选框是否被选中,假设选中,则设置cookie对象7天生效)if($("#rememberMe").prop("checked")){// 选中,则设置cookie对象7天生效// 将用户信息设置到cookie中
- $.cookie("userIdStr", result.result.userIdStr,{expires:7});
- $.cookie("userName", result.result.userName,{expires:7});
- $.cookie("trueName", result.result.trueName,{expires:7});}else{// 将用户信息设置到cookie中
- $.cookie("userIdStr", result.result.userIdStr);
- $.cookie("userName", result.result.userName);
- $.cookie("trueName", result.result.trueName);}
复制代码 退出登录时,删除前端Cookie即可
<3>全局统一的异常处置及非法恳求的拦截
(1)统一的异常处置
全局异常实现思路:
控制层的方法返回的内容两种情况
视图:视图异常Json:方法执行错误 返回错误json信息
全局异常拦截器的实现,简化了try-catch代码
实现 HandlerExceptionResolver 接口 ,处置应用程序异常信息
(2)非法恳求拦截
对于后端菜单资源,这里要求用户必需停止登录来维护 web 资源的安全性,此时引入非法恳求拦截功
能。
实现思路:
判断用户是否是登录状态
获取Cookie对象,解析用户ID的值
假设用户ID不为空,且在数据库中存在对应的用户记录,表示恳求合法
否则,恳求不合法,停止拦截,重定向到登录页面
定义拦截器:在新建 interceptors 包,创建 NoLoginInterceptor 类,并继承 HandlerInterceptorAdapter 适配器,
实现拦截器功能。- /**
- * 非法访问拦截
- * 继承HandlerInterceptorAdapter适配器
- */publicclassNoLoginInterceptorextendsHandlerInterceptorAdapter{@AutowiredprivateUserMapper userMapper;/**
- * 拦截用户是否是登录状态
- * 在目的方法(资源)执行前执行的方法
- * 返回boolean
- * 假设为true,表示目的方法可用被执行
- * 假设为false,表示阻止目的方法执行
- *
- * @param request
- * @param response
- * @param handler
- * @return
- * @throws Exception
- */@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{//获取cookie中的用户IdInteger userId =LoginUserUtil.releaseUserIdFromCookie(request);//判断用户Id是否为空,且数据库中是否存在改userId的记录if(userId ==null|| userMapper.selectByPrimaryKey(userId)==null){//抛出未登录异常thrownewNoLoginException();}returntrue;}}
复制代码 全局异常类配置:在全局异常处置类中引入未登录异常判断- /**
- * 全局异常统一处置
- */@ComponentpublicclassGlobalExceptionResolverimplementsHandlerExceptionResolver{/**
- * 异常处置方法
- * 方法的返回值:
- * 1. 返回视图
- * 2. 返回数据(JSON数据)
- * <p>
- * 如何判断方法的返回值?
- * 通过方法上是否声明@ResponseBody注解
- * 假设未声明,则表示返回视图
- * 假设声明了,则表示返回数据
- *
- * @param request request恳求对象
- * @param response response响应对象
- * @param handler 方法对象
- * @param ex 异常对象
- * @return org.springframework.web.servlet.ModelAndView
- */@OverridepublicModelAndViewresolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex){/**
- * 非法恳求拦截
- * 判断是否抛出未登录异常
- * 假设抛出该异常,则要求用户登录,重定向跳转到登录页面
- */if(ex instanceofNoLoginException){// 重定向到登录页面ModelAndView mv =newModelAndView("redirect:/index");return mv;}/**
- * 设置默认异常处置(返回视图)
- */ModelAndView modelAndView =newModelAndView("error");// 设置异常信息
- modelAndView.addObject("code",500);
- modelAndView.addObject("msg","系统异常,请重试...");// 判断HandlerMethodif(handler instanceofHandlerMethod){// 类型转换HandlerMethod handlerMethod =(HandlerMethod) handler;// 获取方法上声明的@ResponseBody注解对象ResponseBody responseBody = handlerMethod.getMethod().getDeclaredAnnotation(ResponseBody.class);// 判断ResponseBody对象是否为空 (假设对象为空,则表示返回的事视图;假设不为空,则表示返回的事数据)if(responseBody ==null){/**
- * 方法返回视图
- */// 判断异常类型if(ex instanceofParamsException){ParamsException p =(ParamsException) ex;// 设置异常信息
- modelAndView.addObject("code", p.getCode());
- modelAndView.addObject("msg", p.getMsg());}elseif(ex instanceofAuthException){// 认证异常AuthException a =(AuthException) ex;// 设置异常信息
- modelAndView.addObject("code", a.getCode());
- modelAndView.addObject("msg", a.getMsg());}return modelAndView;}else{/**
- * 方法返回数据
- */// 设置默认的异常处置ResultInfo resultInfo =newResultInfo();
- resultInfo.setCode(500);
- resultInfo.setMsg("异常异常,请重试!");// 判断异常类型是否是自定义异常if(ex instanceofParamsException){ParamsException p =(ParamsException) ex;
- resultInfo.setCode(p.getCode());
- resultInfo.setMsg(p.getMsg());}elseif(ex instanceofAuthException){// 认证异常AuthException a =(AuthException) ex;
- resultInfo.setCode(a.getCode());
- resultInfo.setMsg(a.getMsg());}// 设置响应类型及编码格式(响应JSON格式的数据)
- response.setContentType("application/json;charset=UTF-8");// 得到字符输出流PrintWriter out =null;try{// 得到输出流
- out = response.getWriter();// 将需要返回的对象转换成JOSN格式的字符String json = JSON.toJSONString(resultInfo);// 输出数据
- out.write(json);}catch(IOException e){
- e.printStackTrace();}finally{// 假设对象不为空,则关闭if(out !=null){
- out.close();}}returnnull;}}return modelAndView;}}
复制代码 拦截器生效配置:- @Configuration//配置类publicclassMvcConfigextendsWebMvcConfigurerAdapter{@Bean//将方法的返回值交给IOCpublicNoLoginInterceptornoLoginInterceptor(){returnnewNoLoginInterceptor();}/**
- * 添加拦截器
- *
- * @param registry
- */@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){//需要实现了拦截器功能的实例对象 NoLoginInterceptor
- registry.addInterceptor(noLoginInterceptor())//设置需要被拦截的资源.addPathPatterns("/**")// 设置不需要被拦截的资源.excludePathPatterns("/css/**","/images/**","/js/**","/lib/**").excludePathPatterns("/index","/user/login");}}
复制代码 测试拦截效果:
当 Cookie 中的用户ID不存在时,访问 main 页面,会自动跳转到登录页面
<3>记住我功能
记住我功能核心在于当用户上次登录时假设点击了记住我,下次在重新翻开阅读器时可以不用选择登
录,此时可以借助拦截器 + cookie 来实现,当用户在登录时,假设用户点击了记住我功能,默认设置
cookie存储时间为7天即可。
2.营销管理模块(CRUD操作,见源码)
<1>功能开发及表构造分析
功能开发:
表构造:
这里注意:时间格式化:- @JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8")@DateTimeFormat(pattern ="yyyy-MM-dd")// 假设传送的参数是Date类型,要求传入的时间字符串的格式privateDate planDate;
复制代码 3.权限管理模块(CRUD操作见源码)
根本概念:RBAC是基于角色的访问控制( Role-Based Access Control )在RBAC中,权限与角色相关联,用户
通过扮演适当的角色从而得到这些角色的权限。这样管理都是层级互相依赖的,权限赋予给角色,角色
又赋予用户,这样的权限设计很清楚,管理起来很方便。
<1>.模块功能及表的构造设计
功能模块:
表构造设计:
从上面实体对应关系分析,权限表设计分为以下根本的五张表构造:用户表(t_user)、角色表(t_role)、
t_user_role(用户角色表)、资源表(t_module)、权限表(t_permission)
用户和角色间一对一关系,角色和权限间一对一关系,建立t_user_role和t_permission中间表
表构造关系如下:
<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";}
复制代码 (1).菜单级别显示控制
系统根据登录用户扮演的不同角色来对登录用户操作的菜单停止动态控制显示操作,这里显示的控制使
用freemarker指令+内建函数实现
例如:
会根据权限码,来显示菜单内容:
(2).后端方法级别访问控制(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;}}
复制代码 4.客户管理模块(CRUD操作见源码)
<1>.模块功能及表构造设计
模块功能:
表构造设计:
t_customer 客户表、t_customer_contact 客户交往记录表、t_customer_linkman 客户联络人表、t_customer_order 客户订单表、t_order_details 订单详情表
<2>.定时器
当实现了客户数据转移业务逻辑代码后,这里需要考虑一个问题:客户数据量的问题随着时间的积累,流失的客户数据可能就比较大,假设数据的获取在用户查询时停止,此时后端对于数据的查询就会变得很慢,此时可以使用我们之前讲到的定时任务来处置,后台通过定时器来对流失客户数据定时停止转移处置,从而当前端用户查询时只需到客户流失表查询流失数据即可。
增加定时器效劳:- /**
- * 定时任务的执行
- */@ComponentpublicclassJobTask{@AutowiredprivateCustomerService customerService;//cron表达式//每两秒执行一次//@Scheduled(cron = "0/2 * * * * ?")//从六月开端,每个月执行一次@Scheduled(cron ="* * * * 6/1 ? ")publicvoidjob(){//调用需要被执行的方法//开端执行定时任务System.out.println("开端执行定时器任务");
- customerService.updateCustomerState();System.out.println("定时器任务执行完成");}}
复制代码 Starter开启定时任务环境配置:- @SpringBootApplication@MapperScan("org.example.crm.dao")//启用定时任务@EnableSchedulingpublicclassStarter{publicstaticvoidmain(String[] args){SpringApplication.run(Starter.class);}}
复制代码 5.效劳管理(CRUD操作见源码)
<1>功能实现及表构造设计
功能实现:
表构造设计:
<2>效劳实现
这里对于效劳管理效劳的创建,分配,处置与反响后端代码实现放在同一个方法中停止处置,同时方便对于效劳状态值统一处置,这里定义 CustomerServeStatus 枚举类来实现。- /**
- * 客户效劳状态枚举类
- */publicenumCustomerServeStatus{// 创建CREATED("fw_001"),// 分配ASSIGNED("fw_002"),// 处置PROCED("fw_003"),// 反响FEED_BACK("fw_004"),// 归档ARCHIVED("fw_005");privateString state;CustomerServeStatus(String state){this.state = state;}publicStringgetState(){return state;}}
复制代码 6.统计报表管理(CRUD见源码)
<1>.功能实现
功能实现:
<2>.使用ECharts对数据停止分析
ECharts官网:添加链接描绘
折线图数据返回实现:- /**
- * 查询客户构成 (折线图)
- * @return
- */publicMap<String,Object>countCustomerMake(){Map<String,Object> map =newHashMap<>();// 查询客户构成数据的列表List<Map<String,Object>> dataList = customerMapper.countCustomerMake();// 折线图X轴数据 数组List<String> data1 =newArrayList<>();// 折线图Y轴数据 数组List<Integer> data2 =newArrayList<>();// 判断数据列表 循环设置数据if(dataList !=null&& dataList.size()>0){for(int i =0; i < dataList.size(); i++){
- data1.add(dataList.get(i).get("level").toString());
- data2.add(Integer.parseInt(dataList.get(i).get("total").toString()));}}// 将X轴的数据集合与Y轴的数据集合,设置到map中
- map.put("data1", data1);
- map.put("data2", data2);return map;}
复制代码
饼状图数据返回实现:- publicMap<String,Object>countCustomerMake02(){Map<String,Object> map =newHashMap<>();// 查询客户构成数据的列表List<Map<String,Object>> dataList = customerMapper.countCustomerMake();// 饼状图数据 数组(数组中是字符串)List<String> data1 =newArrayList<>();// 饼状图的数据 数组(数组中是对象)List<Map<String,Object>> data2 =newArrayList<>();// 判断数据列表 循环设置数据if(dataList !=null&& dataList.size()>0){// 遍历集合for(int i =0; i < dataList.size(); i++){//饼状图数据, 数组(数组中是字符串
- data1.add(dataList.get(i).get("level").toString());//饼状图数据 数组(数组中是对象)Map<String,Object> dataMap =newHashMap<>();
- dataMap.put("name", dataList.get(i).get("level"));
- dataMap.put("value", dataList.get(i).get("total"));
- data2.add(dataMap);}}// 将X轴的数据集合与Y轴的数据集合,设置到map中
- map.put("data1", data1);
- map.put("data2", data2);return map;}
复制代码
欢送Star CRM客户管理系统 |
|