黑馬程序員-傳智健康項目(第十章)


傳智健康項目


第10章 權限控制、圖形報表

1. 在項目中應用Spring Security

前面我們已經學習了Spring Security框架的使用方法,本章節我們就需要將Spring Security框架應用到后台系統中進行權限控制,其本質就是認證和授權。

要進行認證和授權需要前面課程中提到的權限模型涉及的7張表支撐,因為用戶信息、權限信息、菜單信息、角色信息、關聯信息等都保存在這7張表中,也就是這些表中的數據是我們進行認證和授權的依據。所以在真正進行認證和授權之前需要對這些數據進行管理,即我們需要開發如下一些功能:

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.itheima.security;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.pojo.CheckItem;
import com.itheima.pojo.Permission;
import com.itheima.pojo.Role;
import com.itheima.service.CheckItemService;
import com.itheima.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.itheima.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.itheima.service;

import com.itheima.pojo.User;
/**
 * 用戶服務接口
 */
public interface UserService {
    public User findByUsername(String username);
}
package com.itheima.service;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.dao.PermissionDao;
import com.itheima.dao.RoleDao;
import com.itheima.dao.UserDao;
import com.itheima.pojo.Permission;
import com.itheima.pojo.Role;
import com.itheima.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.itheima.dao;

import com.itheima.pojo.User;

public interface UserDao {
    public User findByUsername(String username);
}
package com.itheima.dao;

import com.itheima.pojo.Role;
import java.util.Set;

public interface RoleDao {
    public Set<role> findByUserId(int id);
}
package com.itheima.dao;

import com.itheima.pojo.Permission;
import java.util.Set;

public interface PermissionDao {
    public Set<permission> findByRoleId(int roleId);
}
<!--?xml version="1.0" encoding="UTF-8" ?-->

<mapper namespace="com.itheima.dao.UserDao">
    <select id="findByUsername" parametertype="string" resulttype="com.itheima.pojo.User">
        select * from t_user where username = #{username}
    </select>
</mapper>
<!--?xml version="1.0" encoding="UTF-8" ?-->

<mapper namespace="com.itheima.dao.RoleDao">
    <select id="findByUserId" parametertype="int" resulttype="com.itheima.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" ?-->

<mapper namespace="com.itheima.dao.PermissionDao">
    <select id="findByRoleId" parametertype="int" resulttype="com.itheima.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.itheima">

注意:此處原來掃描的包為com.itheima.controller,現在改為com.itheima包的目的是需要將我們上面定義的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:form-login></security:intercept-url></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:password-encoder></security:authentication-provider>
    </security:authentication-manager>

    <!--開啟注解方式權限控制-->
    <security:global-method-security pre-post-annotations="enabled">
</security:global-method-security></bean></security:http></security:http></security:http></security:http></beans>

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

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

第六步:在Controller的方法上加入權限控制注解,此處以CheckItemController為例

package com.itheima.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.constant.MessageConstant;
import com.itheima.constant.PermissionConstant;
import com.itheima.entity.PageResult;
import com.itheima.entity.QueryPageBean;
import com.itheima.entity.Result;
import com.itheima.exception.CustomException;
import com.itheima.pojo.CheckItem;
import com.itheima.pojo.Member;
import com.itheima.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.itheima.controller;

import com.itheima.constant.MessageConstant;
import com.itheima.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

下載完成可以得到如下文件:

解壓上面的zip文件:

我們只需要將dist目錄下的echarts.js文件引入到頁面上就可以使用了

2.2 5分鍾上手ECharts

我們可以參考官方提供的5分鍾上手ECharts文檔感受一下ECharts的使用方式,地址如下:

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

第一步:創建html頁面並引入echarts.js文件




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


第二步:在頁面中准備一個具備寬高的DOM容器。


    <!-- 為 ECharts 准備一個具備大小(寬高)的 DOM -->
    <div id="main" style="width: 600px;height:400px;"></div>

第三步:通過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.itheima.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.constant.MessageConstant;
import com.itheima.entity.Result;
import com.itheima.pojo.Setmeal;
import com.itheima.service.MemberService;
import com.itheima.service.ReportService;
import com.itheima.service.SetmealService;
import com.itheima.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 <= #{value}
</select>

視頻地址:https://www.bilibili.com/video/BV1Bo4y117zV


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM