傳智健康項目
- 黑馬程序員-傳智健康項目(第一章)
- 黑馬程序員-傳智健康項目(第二章)
- 黑馬程序員-傳智健康項目(第三章)
- 黑馬程序員-傳智健康項目(第四章)
- 黑馬程序員-傳智健康項目(第五章)
- 黑馬程序員-傳智健康項目(第六章)
- 黑馬程序員-傳智健康項目(第七章)
- 黑馬程序員-傳智健康項目(第八章)
- 黑馬程序員-傳智健康項目(第九章)
- 黑馬程序員-傳智健康項目(第十章)
- 黑馬程序員-傳智健康項目(第十一章)
- 黑馬程序員-傳智健康項目(第十二章)
- 黑馬程序員-傳智健康項目(第十三章)
- 黑馬程序員-傳智健康項目資料
第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/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>