首页 前端知识 Spring Security和ECharts实现权限控制、图形报表生成,Java,JavaScript,HTML源码

Spring Security和ECharts实现权限控制、图形报表生成,Java,JavaScript,HTML源码

2024-03-05 09:03:14 前端知识 前端哥 467 783 我要收藏
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:security="http://www.springframework.org/schema/security"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
                         http://www.springframework.org/schema/security
                         http://www.springframework.org/schema/security/spring-security.xsd">
   <!--
       http:用于定义相关权限控制
       指定哪些资源不需要进行权限校验,可以使用通配符
   -->
   <security:http security="none" pattern="/js/**" />
   <security:http security="none" pattern="/css/**" />
   <security:http security="none" pattern="/img/**" />
   <security:http security="none" pattern="/plugins/**" />
 
   <!--
       http:用于定义相关权限控制
       auto-config:是否自动配置
                       设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出处理等
                       设置为false时需要显示提供登录表单配置,否则会报错
       use-expressions:用于指定intercept-url中的access属性是否使用表达式
   -->
   <security:http auto-config="true" use-expressions="true">
       <security:headers>
           <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
           <security:frame-options policy="SAMEORIGIN"></security:frame-options>
       </security:headers>
       <!--
           intercept-url:定义一个拦截规则
           pattern:对哪些url进行权限控制
           access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,
 请求的用户只需拥有其中的一个角色就能成功访问对应的URL
           isAuthenticated():已经经过认证(不是匿名用户)
       -->
       <security:intercept-url pattern="/pages/**"  access="isAuthenticated()" />
       <!--form-login:定义表单登录信息-->
       <security:form-login login-page="/login.html"
                            username-parameter="username"
                            password-parameter="password"
                            login-processing-url="/login.do"
                            default-target-url="/pages/main.html"
                            always-use-default-target="true"
                            authentication-failure-url="/login.html"
       />
       
       <!--
           csrf:对应CsrfFilter过滤器
           disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,
否则登录操作会被禁用(403
       -->
       <security:csrf disabled="true"></security:csrf>
   </security:http>
   <!--配置密码加密对象-->
   <bean id="passwordEncoder"
         class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
   <!--认证管理器,用于处理认证操作-->
   <security:authentication-manager>
       <!--认证提供者,执行具体的认证逻辑-->
       <security:authentication-provider user-service-ref="springSecurityUserService">
           <!--指定密码加密策略-->
           <security:password-encoder ref="passwordEncoder" />
       </security:authentication-provider>
   </security:authentication-manager>
   <!--开启注解方式权限控制-->
   <security:global-method-security pre-post-annotations="enabled" />
</beans>
复制

1. 在项目中应用Spring Security

在真正进行认证和授权之前需要对数据进行管理,即我们需要开发如下一些功能:

1、权限数据管理(增删改查)

2、菜单数据管理(增删改查)

3、角色数据管理(增删改查、角色关联权限、角色关联菜单)

4、用户数据管理(增删改查、用户关联角色)

1.1 导入Spring Security环境

第一步:在health_parent父工程的pom.xml中导入Spring Security的maven坐标

<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-web</artifactId>
 <version>${spring.security.version}</version>
</dependency>
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-config</artifactId>
 <version>${spring.security.version}</version>
</dependency>
复制

复制

第二步:在health_backend工程的web.xml文件中配置用于整合Spring Security框架的过滤器DelegatingFilterProxy

<!--委派过滤器,用于整合其他框架-->
<filter>
 <!--整合spring security时,此过滤器的名称固定springSecurityFilterChain-->
 <filter-name>springSecurityFilterChain</filter-name>
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
 <filter-name>springSecurityFilterChain</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>
复制

复制

1.2 实现认证和授权

第一步:在health_backend工程中按照Spring Security框架要求提供SpringSecurityUserService,并且实现UserDetailsService接口

package com.test.security;
import com.test.dubbo.config.annotation.Reference;
import com.test.pojo.CheckItem;
import com.test.pojo.Permission;
import com.test.pojo.Role;
import com.test.service.CheckItemService;
import com.test.service.UserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Component
public class SpringSecurityUserService implements UserDetailsService{
   @Reference //注意:此处要通过dubbo远程调用用户服务
   private UserService userService;
 
   //根据用户名查询用户信息
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    //远程调用用户服务,根据用户名查询用户信息
       com.test.pojo.User user = userService.findByUsername(username);
       if(user == null){
             //用户名不存在
             return null;
      }
       List<GrantedAuthority> list = new ArrayList<>();
       Set<Role> roles = user.getRoles();
       for(Role role : roles){
           //授予角色
           list.add(new SimpleGrantedAuthority(role.getKeyword()));
           Set<Permission> permissions = role.getPermissions();
           for(Permission permission : permissions){
            //授权
               list.add(new SimpleGrantedAuthority(permission.getKeyword()));
          }
      }
       
       UserDetails userDetails = new User(username,user.getPassword(),list);
       return userDetails;
  }
}
复制

第二步:创建UserService服务接口、服务实现类、Dao接口、Mapper映射文件等

package com.test.service;
import com.test.pojo.User;
/**
* 用户服务接口
*/
public interface UserService {
   public User findByUsername(String username);
}
复制
package com.test.service;
import com.test.dubbo.config.annotation.Service;
import com.test.dao.PermissionDao;
import com.test.dao.RoleDao;
import com.test.dao.UserDao;
import com.test.pojo.Permission;
import com.test.pojo.Role;
import com.test.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.Set;
@Service(interfaceClass = UserService.class)
@Transactional
public class UserServiceImpl implements UserService {
   @Autowired
   private UserDao userDao;
   @Autowired
   private RoleDao roleDao;
   @Autowired
   private PermissionDao permissionDao;
   public User findByUsername(String username) {
       User user = userDao.findByUsername(username);
       if(user == null){
           return null;
      }
       Integer userId = user.getId();
       Set<Role> roles = roleDao.findByUserId(userId);
       if(roles != null && roles.size() > 0){
           for(Role role : roles){
               Integer roleId = role.getId();
               Set<Permission> permissions = permissionDao.findByRoleId(roleId);
               if(permissions != null && permissions.size() > 0){
                   role.setPermissions(permissions);
              }
          }
           user.setRoles(roles);
      }
       return user;
  }
}
复制
package com.test.dao;
import com.test.pojo.User;
public interface UserDao {
   public User findByUsername(String username);
}
复制
package com.test.dao;
import com.test.pojo.Role;
import java.util.Set;
public interface RoleDao {
   public Set<Role> findByUserId(int id);
}
复制
package com.test.dao;
import com.test.pojo.Permission;
import java.util.Set;
public interface PermissionDao {
   public Set<Permission> findByRoleId(int roleId);
}
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.dao.UserDao" >
   <select id="findByUsername"
           parameterType="string"
           resultType="com.test.pojo.User">
      select * from t_user where username = #{username}
   </select>
</mapper>
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.dao.RoleDao" >
   <select id="findByUserId"
           parameterType="int"
           resultType="com.test.pojo.Role">
      select r.*
    from t_role r ,t_user_role ur
    where r.id = ur.role_id and ur.user_id = #{userId}
   </select>
</mapper>
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.dao.PermissionDao" >
   <select id="findByRoleId"
           parameterType="int"
           resultType="com.test.pojo.Permission">
      select p.*
    from t_permission p ,t_role_permission rp
    where p.id = rp.permission_id and rp.role_id = #{roleId}
   </select>
</mapper>
复制
复制

第三步:修改health_backend工程中的springmvc.xml文件,修改dubbo批量扫描的包路径

<!--批量扫描-->
<dubbo:annotation package="com.test" />
复制

注意:此处原来扫描的包为com.test.controller,现在改为com.test包的目的是需要将我们上面定义的SpringSecurityUserService也扫描到,因为在SpringSecurityUserService的loadUserByUsername方法中需要通过dubbo远程调用名称为UserService的服务。

第四步:在health_backend工程中提供spring-security.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:security="http://www.springframework.org/schema/security"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
                         http://www.springframework.org/schema/security
                         http://www.springframework.org/schema/security/spring-security.xsd">
   <!--
       http:用于定义相关权限控制
       指定哪些资源不需要进行权限校验,可以使用通配符
   -->
   <security:http security="none" pattern="/js/**" />
   <security:http security="none" pattern="/css/**" />
   <security:http security="none" pattern="/img/**" />
   <security:http security="none" pattern="/plugins/**" />
 
   <!--
       http:用于定义相关权限控制
       auto-config:是否自动配置
                       设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出处理等
                       设置为false时需要显示提供登录表单配置,否则会报错
       use-expressions:用于指定intercept-url中的access属性是否使用表达式
   -->
   <security:http auto-config="true" use-expressions="true">
       <security:headers>
           <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
           <security:frame-options policy="SAMEORIGIN"></security:frame-options>
       </security:headers>
       <!--
           intercept-url:定义一个拦截规则
           pattern:对哪些url进行权限控制
           access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,
 请求的用户只需拥有其中的一个角色就能成功访问对应的URL
           isAuthenticated():已经经过认证(不是匿名用户)
       -->
       <security:intercept-url pattern="/pages/**"  access="isAuthenticated()" />
       <!--form-login:定义表单登录信息-->
       <security:form-login login-page="/login.html"
                            username-parameter="username"
                            password-parameter="password"
                            login-processing-url="/login.do"
                            default-target-url="/pages/main.html"
                            always-use-default-target="true"
                            authentication-failure-url="/login.html"
       />
       
       <!--
           csrf:对应CsrfFilter过滤器
           disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,
否则登录操作会被禁用(403
       -->
       <security:csrf disabled="true"></security:csrf>
   </security:http>
   <!--配置密码加密对象-->
   <bean id="passwordEncoder"
         class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
   <!--认证管理器,用于处理认证操作-->
   <security:authentication-manager>
       <!--认证提供者,执行具体的认证逻辑-->
       <security:authentication-provider user-service-ref="springSecurityUserService">
           <!--指定密码加密策略-->
           <security:password-encoder ref="passwordEncoder" />
       </security:authentication-provider>
   </security:authentication-manager>
   <!--开启注解方式权限控制-->
   <security:global-method-security pre-post-annotations="enabled" />
</beans>
复制

第五步:在springmvc.xml文件中引入spring-security.xml文件

<import resource="spring-security.xml"></import>
复制

第六步:在Controller的方法上加入权限控制注解,此处以CheckItemController为例

package com.test.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.test.constant.MessageConstant;
import com.test.constant.PermissionConstant;
import com.test.entity.PageResult;
import com.test.entity.QueryPageBean;
import com.test.entity.Result;
import com.test.exception.CustomException;
import com.test.pojo.CheckItem;
import com.test.pojo.Member;
import com.test.service.CheckItemService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import java.util.List;
/**
* 体检检查项管理
*/
@RestController
@RequestMapping("/checkitem")
public class CheckItemController {
   @Reference
   private CheckItemService checkItemService;
   //分页查询
   @PreAuthorize("hasAuthority('CHECKITEM_QUERY')")//权限校验
   @RequestMapping("/findPage")
   public PageResult findPage(@RequestBody QueryPageBean queryPageBean){
       PageResult pageResult = checkItemService.pageQuery(
                                   queryPageBean.getCurrentPage(),
                                   queryPageBean.getPageSize(),
                                   queryPageBean.getQueryString());
       return pageResult;
  }
   //删除
   @PreAuthorize("hasAuthority('CHECKITEM_DELETE')")//权限校验
   @RequestMapping("/delete")
   public Result delete(Integer id){
       try {
           checkItemService.delete(id);
      }catch (RuntimeException e){
           return new Result(false,e.getMessage());
      }catch (Exception e){
           return new Result(false, MessageConstant.DELETE_CHECKITEM_FAIL);
      }
       return new Result(true,MessageConstant.DELETE_CHECKITEM_SUCCESS);
  }
   //新增
   @PreAuthorize("hasAuthority('CHECKITEM_ADD')")//权限校验
   @RequestMapping("/add")
   public Result add(@RequestBody CheckItem checkItem){
       try {
           checkItemService.add(checkItem);
      }catch (Exception e){
           return new Result(false,MessageConstant.ADD_CHECKITEM_FAIL);
      }
       return new Result(true,MessageConstant.ADD_CHECKITEM_SUCCESS);
  }
   //编辑
   @PreAuthorize("hasAuthority('CHECKITEM_EDIT')")//权限校验
   @RequestMapping("/edit")
   public Result edit(@RequestBody CheckItem checkItem){
       try {
           checkItemService.edit(checkItem);
      }catch (Exception e){
           return new Result(false,MessageConstant.EDIT_CHECKITEM_FAIL);
      }
       return new Result(true,MessageConstant.EDIT_CHECKITEM_SUCCESS);
  }
}
复制

第七步:修改页面,没有权限时提示信息设置,此处以checkitem.html中的handleDelete方法为例

//权限不足提示
showMessage(r){
 if(r == 'Error: Request failed with status code 403'){
   //权限不足
   this.$message.error('无访问权限');
   return;
}else{
   this.$message.error('未知错误');
   return;
}
}
// 删除
handleDelete(row) {
 this.$confirm('此操作将永久当前数据,是否继续?', '提示', {
   type: 'warning'
}).then(()=>{
   //点击确定按钮执行此代码
   axios.get("/checkitem/delete.do?id=" + row.id).then((res)=> {
     if(!res.data.flag){
       //删除失败
       this.$message.error(res.data.message);
    }else{
       //删除成功
       this.$message({
         message: res.data.message,
         type: 'success'
      });
       this.findPage();
    }
  }).catch((r)=>{
     this.showMessage(r);
  });
}).catch(()=> {
   //点击取消按钮执行此代码
   this.$message('操作已取消');
});
}
复制

1.3 显示用户名

前面我们已经完成了认证和授权操作,如果用户认证成功后需要在页面展示当前用户的用户名。Spring Security在认证成功后会将用户信息保存到框架提供的上下文对象中,所以此处我们就可以调用Spring Security框架提供的API获取当前用户的username并展示到页面上。

实现步骤:

第一步:在main.html页面中修改,定义username模型数据基于VUE的数据绑定展示用户名,发送ajax请求获取username

<script>
   new Vue({
       el: '#app',
       data:{
           username:null,//用户名
     menuList:[]
      },
       created(){
           //发送请求获取当前登录用户的用户名
           axios.get('/user/getUsername.do').then((response)=>{
               this.username = response.data.data;
          });
      }
  });
</script>
<div class="avatar-wrapper">
 <img src="../img/user2-160x160.jpg" class="user-avatar">
 <!--展示用户名-->
{{username}}
</div>
复制

第二步:创建UserController并提供getUsername方法

package com.test.controller;
import com.test.constant.MessageConstant;
import com.test.entity.Result;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
   //获取当前登录用户的用户名
   @RequestMapping("/getUsername")
   public Result getUsername()throws Exception{
       try{
           org.springframework.security.core.userdetails.User user =
                  (org.springframework.security.core.userdetails.User)
                   SecurityContextHolder.getContext().getAuthentication().getPrincipal();
           return new Result(true, MessageConstant.GET_USERNAME_SUCCESS,user.getUsername());
      }catch (Exception e){
           return new Result(false, MessageConstant.GET_USERNAME_FAIL);
      }
  }
}
复制

通过debug调试可以看到Spring Security框架在其上下文中保存的用户相关信息:

1.4 用户退出

第一步:在main.html中提供的退出菜单上加入超链接

<el-dropdown-item divided>
 <span style="display:block;"><a href="/logout.do">退出</a></span>
</el-dropdown-item>
复制

第二步:在spring-security.xml文件中配置

<!--
 logout:退出登录
 logout-url:退出登录操作对应的请求路径
 logout-success-url:退出登录后的跳转页面
-->
<security:logout logout-url="/logout.do"
                logout-success-url="/login.html" invalidate-session="true"/>
复制

2. 图形报表ECharts

2.1 ECharts简介

ECharts缩写来自Enterprise Charts,商业级数据图表,是百度的一个开源的使用JavaScript实现的数据可视化工具,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖轻量级的矢量图形库 ZRender,提供直观、交互丰富、可高度个性化定制的数据可视化图表。

官网:https://echarts.baidu.com/

下载地址:https://echarts.baidu.com/download.html

2.2 5分钟上手ECharts

我们可以参考官方提供的5分钟上手ECharts文档感受一下ECharts的使用方式,地址如下:

https://www.echartsjs.com/tutorial.html#5 分钟上手 ECharts

第一步:创建html页面并引入echarts.js文件

<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8">
   <!-- 引入 ECharts 文件 -->
   <script src="echarts.js"></script>
</head>
</html>
复制

第二步:在页面中准备一个具备宽高的DOM容器。

<body>
   <!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
   <div id="main" style="width: 600px;height:400px;"></div>
</body>
复制

第三步:通过echarts.init方法初始化一个 echarts 实例并通过setOption方法生成一个简单的柱状图

<script type="text/javascript">
 // 基于准备好的dom,初始化echarts实例
 var myChart = echarts.init(document.getElementById('main'));
 // 指定图表的配置项和数据
 var option = {
   title: {
     text: 'ECharts 入门示例'
  },
   tooltip: {},
   legend: {
     data:['销量']
  },
   xAxis: {
     data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
  },
   yAxis: {},
   series: [{
     name: '销量',
     type: 'bar',
     data: [5, 20, 36, 10, 10, 20]
  }]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
复制

效果如下:

2.3 查看ECharts官方实例

ECharts提供了很多官方实例,我们可以通过这些官方实例来查看展示效果和使用方法。

官方实例地址:https://www.echartsjs.com/examples/

可以点击具体的一个图形会跳转到编辑页面,编辑页面左侧展示源码(js部分源码),右侧展示图表效果。

要查看完整代码可以点击右下角的Download按钮将完整页面下载到本地。

通过官方案例我们可以发现,使用ECharts展示图表效果,关键点在于确定此图表所需的数据格式,然后按照此数据格式提供数据就可以了,我们无须关注效果是如何渲染出来的。

在实际应用中,我们要展示的数据往往存储在数据库中,所以我们可以发送ajax请求获取数据库中的数据并转为图表所需的数据即可。

3. 会员数量折线图

3.1 需求分析

会员信息是体检机构的核心数据,其会员数量和增长数量可以反映出机构的部分运营情况。通过折线图可以直观的反映出会员数量的增长趋势。本章节我们需要展示过去一年时间内每个月的会员总数据量。展示效果如下图:

3.2 完善页面

会员数量折线图对应的页面为/pages/report_member.html。

3.2.1 导入ECharts库

第一步:将echarts.js文件复制到health_backend工程的plugins目录下

第二步:在report_member.html页面引入echarts.js文件

<script src="../plugins/echarts/echarts.js"></script>
复制

3.2.2 参照官方实例导入折线图
<div class="box">
 <!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
 <div id="chart1" style="height:600px;"></div>
</div>
<script type="text/javascript">
 // 基于准备好的dom,初始化echarts实例
 var myChart1 = echarts.init(document.getElementById('chart1'));
 //发送ajax请求获取动态数据
 axios.get("/report/getMemberReport.do").then((res)=>{
   myChart1.setOption(
    {
       title: {
         text: '会员数量'
      },
       tooltip: {},
       legend: {
         data:['会员数量']
      },
       xAxis: {
         data: res.data.data.months
      },
       yAxis: {
         type:'value'
      },
       series: [{
         name: '会员数量',
         type: 'line',
         data: res.data.data.memberCount
      }]
    });
});
</script>
复制

根据折线图对数据格式的要求,我们发送ajax请求,服务端需要返回如下格式的数据:

{
"data":{
"months":["2019.01","2019.02","2019.03","2019.04"],
"memberCount":[3,4,8,10]
  },
"flag":true,
"message":"获取会员统计数据成功"
}
复制

3.3 后台代码

3.3.1 Controller

在health_backend工程中创建ReportController并提供getMemberReport方法

package com.test.controller;
import com.test.dubbo.config.annotation.Reference;
import com.test.constant.MessageConstant;
import com.test.entity.Result;
import com.test.pojo.Setmeal;
import com.test.service.MemberService;
import com.test.service.ReportService;
import com.test.service.SetmealService;
import com.test.utils.DateUtils;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 统计报表
*/
@RestController
@RequestMapping("/report")
public class ReportController {
   @Reference
   private MemberService memberService;
   /**
    * 会员数量统计
    * @return
    */
   @RequestMapping("/getMemberReport")
   public Result getMemberReport(){
       Calendar calendar = Calendar.getInstance();
       calendar.add(Calendar.MONTH,-12);//获得当前日期之前12个月的日期
       List<String> list = new ArrayList<>();
       for(int i=0;i<12;i++){
           calendar.add(Calendar.MONTH,1);
           list.add(new SimpleDateFormat("yyyy.MM").format(calendar.getTime()));
      }
       Map<String,Object> map = new HashMap<>();
       map.put("months",list);
       List<Integer> memberCount = memberService.findMemberCountByMonth(list);
       map.put("memberCount",memberCount);
       return new Result(true, MessageConstant.GET_MEMBER_NUMBER_REPORT_SUCCESS,map);
  }
}
复制

3.3.2 服务接口

在MemberService服务接口中扩展方法findMemberCountByMonth

public List<Integer> findMemberCountByMonth(List<String> month);
复制

3.2.3 服务实现类

在MemberServiceImpl服务实现类中实现findMemberCountByMonth方法

//根据月份统计会员数量
public List<Integer> findMemberCountByMonth(List<String> month) {
 List<Integer> list = new ArrayList<>();
 for(String m : month){
   m = m + ".31";//格式:2019.04.31
   Integer count = memberDao.findMemberCountBeforeDate(m);
   list.add(count);
}
 return list;
}
复制

3.3.4 Dao接口

在MemberDao接口中扩展方法findMemberCountBeforeDate

public Integer findMemberCountBeforeDate(String date);
复制

3.3.5 Mapper映射文件

在MemberDao.xml映射文件中提供SQL语句

<!--根据日期统计会员数,统计指定日期之前的会员数-->
<select id="findMemberCountBeforeDate" parameterType="string" resultType="int">
select count(id) from t_member where regTime &lt;= #{value}
</select>
复制
转载请注明出处或者链接地址:https://www.qianduange.cn//article/3260.html
评论
发布的文章

jQuery实现轮播图

2024-02-26 20:02:24

响应式布局的五种方法

2024-03-27 10:03:14

前端实现3D旋转木马相册

2024-03-27 10:03:07

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!