1. 套餐预约占比饼形图
1.1 需求分析
会员可以通过移动端自助进行体检预约,在预约时需要选择预约的体检套餐。本章节我们需要通过饼形
图直观的展示出会员预约的各个套餐占比情况。展示效果如下图:
1.2 完善页面
- 导入ECharts库
- 发送请求查询,返回值赋值给两个数据模型
- 没有匹配的对象,用map封装,setmealCount查询出来的套餐有name,可以赋值给前面setmealNames数据模型
- 重点是sql语句,按名字分组,然后用cout来
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>传智健康</title>
<meta name="description" content="传智健康">
<meta name="keywords" content="传智健康">
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<!-- 引入样式 -->
<link rel="stylesheet" href="../css/style.css">
<script src="../plugins/echarts/echarts.js"></script>
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>统计分析<small>套餐占比</small></h1>
<el-breadcrumb separator-class="el-icon-arrow-right" class="breadcrumb">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>统计分析</el-breadcrumb-item>
<el-breadcrumb-item>套餐占比</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="app-container">
<div class="box">
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="chart1" style="height:600px;"></div>
</div>
</div>
</div>
</body>
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart1 = echarts.init(document.getElementById('chart1'));
// 使用刚指定的配置项和数据显示图表。
//myChart.setOption(option);
axios.get("/report/getSetmealReport.do").then((res)=>{
myChart1.setOption({
title : {
text: '套餐预约占比',
subtext: '',
x:'center'
},
tooltip : {//提示框组件
trigger: 'item',//触发类型,在饼形图中为item
formatter: "{a} <br/>{b} : {c} ({d}%)"//提示内容格式
},
legend: {
orient: 'vertical',
left: 'left',
data: res.data.data.setmealNames
},
series : [
{
name: '套餐预约占比',
type: 'pie',
radius : '55%',
center: ['50%', '60%'],
data:res.data.data.setmealCount,
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
});
</script>
</html>
根据饼形图对数据格式的要求,我们发送ajax请求,服务端需要返回如下格式的数据
{
"data":{"
setmealNames":["套餐1","套餐2","套餐3"],
"setmealCount":[{"name":"套餐1","value":10},
{"name":"套餐2","value":30},
{"name":"套餐3","value":25} ] },
"flag":true,
"message":"获取套餐统计数据成功" }
1.3后端代码
@RestController
@RequestMapping("/report")
public class ReportController {
@Reference
private SetmealService setmealService;
//套餐预约占比,饼图
@RequestMapping("/getSetmealReport")
public Result getSetmealReport() {
try {
Map<String, Object> data = new HashMap<>();
//setmealNames setmealCount
List<String> setmealNames = new ArrayList<>();
List<Map<String, Object>> setmealCount = setmealService.findSetmealCount();
for (Map<String, Object> map : setmealCount) {
String name = (String) map.get("name");
setmealNames.add(name);
}
data.put("setmealNames", setmealNames);
data.put("setmealCount", setmealCount);
return new Result(true, MessageConstant.GET_SETMEAL_LIST_SUCCESS, data);
} catch (Exception e) {
e.printStackTrace();
return new Result(false, MessageConstant.GET_SETMEAL_LIST_FAIL);
}
}
service
public interface ReportService {
Map<String,Object> getBusinessReportData();
}
serviceImpl
//查询套餐预约占比数据
@Override
public List<Map<String, Object>> findSetmealCount() {
return setmealDao.findSetmealCount();
}
dao
public List<Map<String,Object>> findSetmealCount();
dao.xml
<select id="findHotSetmeal" resultType="java.util.Map">
select s.name,count(o.id) setmeal_count, count(o.id)/(select count(id) from t_order)*100 proportion
from t_order o,t_setmeal s where
o.setmeal_id=s.id
group by o.setmeal_id
order by setmeal_count desc
limit 3
</select>
2. 运营数据统计
2.1 需求分析
2.2完善页面
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>传智健康</title>
<meta name="description" content="传智健康">
<meta name="keywords" content="传智健康">
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min(1).css">
<link rel="stylesheet" href="../css/style.css">
<style>
.grid-content {
border-radius: 4px;
min-height: 40px;
}
</style>
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>统计分析<small>运营数据</small></h1>
<el-breadcrumb separator-class="el-icon-arrow-right" class="breadcrumb">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>统计分析</el-breadcrumb-item>
<el-breadcrumb-item>运营数据</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="app-container">
<div class="box" style="height: 900px">
<div class="excelTitle" >
<el-button @click="exportExcel">导出Excel</el-button>运营数据统计
</div>
<div class="excelTime">日期:{{reportData.reportDate}}</div>
<table class="exceTable" cellspacing="0" cellpadding="0">
<tr>
<td colspan="4" class="headBody">会员数据统计</td>
</tr>
<tr>
<td width='20%' class="tabletrBg">新增会员数</td>
<td width='30%'>{{reportData.todayNewMember}}</td>
<td width='20%' class="tabletrBg">总会员数</td>
<td width='30%'>{{reportData.totalMember}}</td>
</tr>
<tr>
<td class="tabletrBg">本周新增会员数</td>
<td>{{reportData.thisWeekNewMember}}</td>
<td class="tabletrBg">本月新增会员数</td>
<td>{{reportData.thisMonthNewMember}}</td>
</tr>
<tr>
<td colspan="4" class="headBody">预约到诊数据统计</td>
</tr>
<tr>
<td class="tabletrBg">今日预约数</td>
<td>{{reportData.todayOrderNumber}}</td>
<td class="tabletrBg">今日到诊数</td>
<td>{{reportData.todayVisitsNumber}}</td>
</tr>
<tr>
<td class="tabletrBg">本周预约数</td>
<td>{{reportData.thisWeekOrderNumber}}</td>
<td class="tabletrBg">本周到诊数</td>
<td>{{reportData.thisWeekVisitsNumber}}</td>
</tr>
<tr>
<td class="tabletrBg">本月预约数</td>
<td>{{reportData.thisMonthOrderNumber}}</td>
<td class="tabletrBg">本月到诊数</td>
<td>{{reportData.thisMonthVisitsNumber}}</td>
</tr>
<tr>
<td colspan="4" class="headBody">热门套餐</td>
</tr>
<tr class="tabletrBg textCenter">
<td>套餐名称</td>
<td>预约数量</td>
<td>占比</td>
<td>备注</td>
</tr>
<tr v-for="s in reportData.hotSetmeal">
<td>{{s.name}}</td>
<td>{{s.setmeal_count}}</td>
<td>{{s.proportion}}</td>
<td></td>
</tr>
</table>
</div>
</div>
</body>
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data:{
reportData:{
reportDate:null,
todayNewMember :0,
totalMember :0,
thisWeekNewMember :0,
thisMonthNewMember :0,
todayOrderNumber :0,
todayVisitsNumber :0,
thisWeekOrderNumber :0,
thisWeekVisitsNumber :0,
thisMonthOrderNumber :0,
thisMonthVisitsNumber :0,
hotSetmeal :[
{name:'阳光爸妈升级肿瘤12项筛查(男女单人)体检套餐',setmeal_count:200,proportion:0.222},
{name:'阳光爸妈升级肿瘤12项筛查体检套餐',setmeal_count:200,proportion:0.222}
]
}
},
created() {
axios.get("/report/getBusinessReportData.do").then((res)=>{
if (res.data.flag){
this.reportData = res.data.data;
}else {
this.$message.error(res.data.message);
}
});
},
methods:{
exportExcel(){
window.location.href = '/report/exportBusinessReport.do';
}
}
})
</script>
</html>
这里重点是数据模型,要把这一块理清楚,然后一个一个赋值,一个一个sql查询,取名字要见名之意
reportData:{
reportDate:null,
todayNewMember :0,
totalMember :0,
thisWeekNewMember :0,
thisMonthNewMember :0,
todayOrderNumber :0,
todayVisitsNumber :0,
thisWeekOrderNumber :0,
thisWeekVisitsNumber :0,
thisMonthOrderNumber :0,
thisMonthVisitsNumber :0,
hotSetmeal :[
{name:'阳光爸妈升级肿瘤12项筛查(男女单人)体检套餐',setmeal_count:200,proportion:0.222},
{name:'阳光爸妈升级肿瘤12项筛查体检套餐',setmeal_count:200,proportion:0.222}
]
}
2.3 Controller
@Reference
private ReportService reportService;
@RequestMapping("/getBusinessReportData")
public Result getBusinessReportData() {
try {
Map<String, Object> data = reportService.getBusinessReportData();
return new Result(true,MessageConstant.GET_BUSINESS_REPORT_SUCCESS,data);
}catch (Exception e){
return new Result(false,MessageConstant.GET_BUSINESS_REPORT_FAIL);
}
}
service
public interface ReportService {
Map<String,Object> getBusinessReportData();
}
serviceImpl
package com.ybb.service.Impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.ybb.dao.MemberDao;
import com.ybb.dao.OrderDao;
import com.ybb.service.ReportService;
import com.ybb.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Description :
* Version :1.0
*/
@Service(interfaceClass = ReportService.class)
@Transactional //查询,可加可不加
public class ReportServiceImpl implements ReportService {
@Autowired
private MemberDao memberDao;
@Autowired
private OrderDao orderDao;
@Override
public Map<String, Object> getBusinessReportData() {
/* reportDate:null,
todayNewMember :0,
totalMember :0,
thisWeekNewMember :0,
thisMonthNewMember :0,
todayOrderNumber :0,
todayVisitsNumber :0,
thisWeekOrderNumber :0,
thisWeekVisitsNumber :0,
thisMonthOrderNumber :0,
thisMonthVisitsNumber :0,
hotSetmeal :[
{name:'阳光爸妈升级肿瘤12项筛查(男女单人)体检套餐',setmeal_count:200,proportion:0.222},
{name:'阳光爸妈升级肿瘤12项筛查体检套餐',setmeal_count:200,proportion:0.222}
] */
try {
Map<String, Object> map = new HashMap<>();
//获得当前日期
String today = DateUtils.parseDate2String(new Date());
map.put("reportDate",today);
//获得本周一的日期
String thisweekMonday = DateUtils.parseDate2String(DateUtils.getThisWeekMonday());
//获得本月第一天的日期
String firstDat4ThisMonth = DateUtils.parseDate2String(DateUtils.getFirstDay4ThisMonth());
//今日新增的会员
Integer todayNewMember = memberDao.findMemberCountByDate(today);
map.put("todayNewMember",todayNewMember);
//总会员数
Integer totalMember = memberDao.findMemberTotalCount();
map.put("totalMember",totalMember);
//本周新增的会员
Integer thisWeekNewMember = memberDao.findMemberCountAfterDate(thisweekMonday);
map.put("thisWeekNewMember",thisWeekNewMember);
//本月新增的会员
Integer thisMonthNewMember = memberDao.findMemberCountAfterDate(firstDat4ThisMonth);
map.put("thisMonthNewMember",thisMonthNewMember);
//今日预约数
Integer todayOrderNumber=orderDao.findOrderCountByDate(today);
map.put("todayOrderNumber",todayOrderNumber);
//今日到诊数
Integer todayVisitsNumber=orderDao.findVisitsCountByDate(today);
map.put("todayVisitfindMemberCountBeforeDatesNumber",todayVisitsNumber);
//本周预约数
Integer thisweekOrderNumber= orderDao.findOrderCountAfterDate(thisweekMonday);
map.put("thisWeekOrderNumber",thisweekOrderNumber);
//本周到诊数
Integer thisweekVisitsNumber= orderDao.findVisitsCountAfterDate(thisweekMonday);
map.put("thisWeekVisitsNumber",thisweekVisitsNumber);
//本月预约数
Integer thisMonthOrderNumber = orderDao.findOrderCountAfterDate(firstDat4ThisMonth);
map.put("thisMonthOrderNumber",thisMonthOrderNumber);
//本月预约到诊数
Integer thisMonthVisitsNumber = orderDao.findVisitsCountAfterDate(firstDat4ThisMonth);
map.put("thisMonthVisitsNumber",thisMonthVisitsNumber);
//热门套餐
List<Map> hotSetmeal = orderDao.findHotSetmeal();
map.put("hotSetmeal",hotSetmeal);
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
工具类
package com.ybb.utils;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 日期操作工具类
*/
public class DateUtils {
/**
* 日期转换- String -> Date
*
* @param dateString 字符串时间
* @return Date类型信息
* @throws Exception 抛出异常
*/
public static Date parseString2Date(String dateString) throws Exception {
if (dateString == null) {
return null;
}
return parseString2Date(dateString, "yyyy-MM-dd");
}
/**
* 日期转换- String -> Date
*
* @param dateString 字符串时间
* @param pattern 格式模板
* @return Date类型信息
* @throws Exception 抛出异常
*/
public static Date parseString2Date(String dateString, String pattern) throws Exception {
if (dateString == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
Date date = sdf.parse(dateString);
return date;
}
/**
* 日期转换 Date -> String
*
* @param date Date类型信息
* @return 字符串时间
* @throws Exception 抛出异常
*/
public static String parseDate2String(Date date) throws Exception {
if (date == null) {
return null;
}
return parseDate2String(date, "yyyy-MM-dd");
}
/**
* 日期转换 Date -> String
*
* @param date Date类型信息
* @param pattern 格式模板
* @return 字符串时间
* @throws Exception 抛出异常
*/
public static String parseDate2String(Date date, String pattern) throws Exception {
if (date == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
String strDate = sdf.format(date);
return strDate;
}
/**
* 获取当前日期的本周一是几号
*
* @return 本周一的日期
*/
public static Date getThisWeekMonday() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
// 获得当前日期是一个星期的第几天
int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
if (1 == dayWeek) {
cal.add(Calendar.DAY_OF_MONTH, -1);
}
// 设置一个星期的第一天,按中国的习惯一个星期的第一天是星期一
cal.setFirstDayOfWeek(Calendar.MONDAY);
// 获得当前日期是一个星期的第几天
int day = cal.get(Calendar.DAY_OF_WEEK);
// 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值
cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
return cal.getTime();
}
/**
* 获取当前日期周的最后一天
*
* @return 当前日期周的最后一天
*/
public static Date getSundayOfThisWeek() {
Calendar c = Calendar.getInstance();
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1;
if (dayOfWeek == 0) {
dayOfWeek = 7;
}
c.add(Calendar.DATE, -dayOfWeek + 7);
return c.getTime();
}
/**
* 根据日期区间获取月份列表
*
* @param minDate 开始时间
* @param maxDate 结束时间
* @return 月份列表
* @throws Exception
*/
public static List<String> getMonthBetween(String minDate, String maxDate, String format) throws Exception {
ArrayList<String> result = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
Calendar min = Calendar.getInstance();
Calendar max = Calendar.getInstance();
min.setTime(sdf.parse(minDate));
min.set(min.get(Calendar.YEAR), min.get(Calendar.MONTH), 1);
max.setTime(sdf.parse(maxDate));
max.set(max.get(Calendar.YEAR), max.get(Calendar.MONTH), 2);
SimpleDateFormat sdf2 = new SimpleDateFormat(format);
Calendar curr = min;
while (curr.before(max)) {
result.add(sdf2.format(curr.getTime()));
curr.add(Calendar.MONTH, 1);
}
return result;
}
/**
* 根据日期获取年度中的周索引
*
* @param date 日期
* @return 周索引
* @throws Exception
*/
public static Integer getWeekOfYear(String date) throws Exception {
Date useDate = parseString2Date(date);
Calendar cal = Calendar.getInstance();
cal.setTime(useDate);
return cal.get(Calendar.WEEK_OF_YEAR);
}
/**
* 根据年份获取年中周列表
*
* @param year 年分
* @return 周列表
* @throws Exception
*/
public static Map<Integer, String> getWeeksOfYear(String year) throws Exception {
Date useDate = parseString2Date(year, "yyyy");
Calendar cal = Calendar.getInstance();
cal.setTime(useDate);
//获取年中周数量
int weeksCount = cal.getWeeksInWeekYear();
Map<Integer, String> mapWeeks = new HashMap<>(55);
for (int i = 0; i < weeksCount; i++) {
cal.get(Calendar.DAY_OF_YEAR);
mapWeeks.put(i + 1, parseDate2String(getFirstDayOfWeek(cal.get(Calendar.YEAR), i)));
}
return mapWeeks;
}
/**
* 获取某年的第几周的开始日期
*
* @param year 年分
* @param week 周索引
* @return 开始日期
* @throws Exception
*/
public static Date getFirstDayOfWeek(int year, int week) throws Exception {
Calendar c = new GregorianCalendar();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, Calendar.JANUARY);
c.set(Calendar.DATE, 1);
Calendar cal = (GregorianCalendar) c.clone();
cal.add(Calendar.DATE, week * 7);
return getFirstDayOfWeek(cal.getTime());
}
/**
* 获取某年的第几周的结束日期
*
* @param year 年份
* @param week 周索引
* @return 结束日期
* @throws Exception
*/
public static Date getLastDayOfWeek(int year, int week) throws Exception {
Calendar c = new GregorianCalendar();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, Calendar.JANUARY);
c.set(Calendar.DATE, 1);
Calendar cal = (GregorianCalendar) c.clone();
cal.add(Calendar.DATE, week * 7);
return getLastDayOfWeek(cal.getTime());
}
/**
* 获取当前时间所在周的开始日期
*
* @param date 当前时间
* @return 开始时间
*/
public static Date getFirstDayOfWeek(Date date) {
Calendar c = new GregorianCalendar();
c.setFirstDayOfWeek(Calendar.SUNDAY);
c.setTime(date);
c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
return c.getTime();
}
/**
* 获取当前时间所在周的结束日期
*
* @param date 当前时间
* @return 结束日期
*/
public static Date getLastDayOfWeek(Date date) {
Calendar c = new GregorianCalendar();
c.setFirstDayOfWeek(Calendar.SUNDAY);
c.setTime(date);
c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6);
return c.getTime();
}
//获得上周一的日期
public static Date geLastWeekMonday(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(getThisWeekMonday(date));
cal.add(Calendar.DATE, -7);
return cal.getTime();
}
//获得本周一的日期
public static Date getThisWeekMonday(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
// 获得当前日期是一个星期的第几天
int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
if (1 == dayWeek) {
cal.add(Calendar.DAY_OF_MONTH, -1);
}
// 设置一个星期的第一天,按中国的习惯一个星期的第一天是星期一
cal.setFirstDayOfWeek(Calendar.MONDAY);
// 获得当前日期是一个星期的第几天
int day = cal.get(Calendar.DAY_OF_WEEK);
// 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值
cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
return cal.getTime();
}
//获得下周一的日期
public static Date getNextWeekMonday(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(getThisWeekMonday(date));
cal.add(Calendar.DATE, 7);
return cal.getTime();
}
//获得今天日期
public static Date getToday(){
return new Date();
}
//获得本月一日的日期
public static Date getFirstDay4ThisMonth(){
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH,1);
return calendar.getTime();
}
public static void main(String[] args) {
try {
System.out.println("本周一" + parseDate2String(getThisWeekMonday()));
System.out.println("本月一日" + parseDate2String(getFirstDay4ThisMonth()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
menberdao
public interface MemberDao {
public List<Member> findAll();
public Page<Member> selectByCondition(String queryString);
public void add(Member member);
public void deleteById(Integer id);
public Member findById(Integer id);
public Member findByTelephone(@Param("phoneNumber") String telephone);
public void edit(Member member);
public Integer findMemberCountBeforeDate(String date);
public Integer findMemberCountByDate(String date);
public Integer findMemberCountAfterDate(String date);
public Integer findMemberTotalCount();
}
memberdao.xml
<?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.ybb.dao.MemberDao" >
<select id="findAll" resultType="com.ybb.pojo.Member">
select * from t_member
</select>
<!--根据条件查询-->
<select id="selectByCondition" parameterType="string" resultType="com.ybb.pojo.Member">
select * from t_member
<if test="value != null and value.length > 0">
where fileNumber = #{value} or phoneNumber = #{value} or name = #{value}
</if>
</select>
<!--新增会员-->
<insert id="add" parameterType="com.ybb.pojo.Member">
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID()
</selectKey>
insert into t_member(fileNumber,name,sex,idCard,phoneNumber,regTime,password,email,birthday,remark)
values (#{fileNumber},#{name},#{sex},#{idCard},#{phoneNumber},#{regTime},#{password},#{email},#{birthday},#{remark})
</insert>
<!--删除会员-->
<delete id="deleteById" parameterType="int">
delete from t_member where id = #{id}
</delete>
<!--根据id查询会员-->
<select id="findById" parameterType="int" resultType="com.ybb.pojo.Member">
select * from t_member where id = #{id}
</select>
<!--根据id查询会员-->
<select id="findByTelephone" parameterType="string" resultType="com.ybb.pojo.Member">
select * from t_member where phoneNumber = #{phoneNumber}
</select>
<!--编辑会员-->
<update id="edit" parameterType="com.ybb.pojo.Member">
update t_member
<set>
<if test="fileNumber != null">
fileNumber = #{fileNumber},
</if>
<if test="name != null">
name = #{name},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="idCard != null">
idCard = #{idCard},
</if>
<if test="phoneNumber != null">
phoneNumber = #{phoneNumber},
</if>
<if test="regTime != null">
regTime = #{regTime},
</if>
<if test="password != null">
password = #{password},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="remark != null">
remark = #{remark},
</if>
</set>
where id = #{id}
</update>
<!--根据日期统计会员数,统计指定日期之前的会员数-->
<select id="findMemberCountBeforeDate" parameterType="string" resultType="int">
select count(id) from t_member where regTime <= #{value}
</select>
<!--根据日期统计会员数-->
<select id="findMemberCountByDate" parameterType="string" resultType="int">
select count(id) from t_member where regTime = #{value}
</select>
<!--根据日期统计会员数,统计指定日期之后的会员数-->
<select id="findMemberCountAfterDate" parameterType="string" resultType="int">
select count(id) from t_member where regTime >= #{value}
</select>
<!--总会员数-->
<select id="findMemberTotalCount" resultType="int">
select count(id) from t_member
</select>
</mapper>
orderdao
package com.ybb.dao;
import com.github.pagehelper.Page;
import com.ybb.pojo.*;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* Created by Administrator
* Date :2020/8/24
* Description :
* Version :1.0
*/
public interface OrderDao {
Map findById4Detail(Integer id);
Member findByCondition(@Param("id") Integer id, @Param("orderDate") Date order_date, @Param("setmeal_id") String setmealId);
void add(Map map);
List<Map> findHotSetmeal();
Integer findOrderCountByDate(String today);
Integer findVisitsCountByDate(String today);
Integer findOrderCountAfterDate(String thisweekMonday);
Integer findVisitsCountAfterDate(String thisweekMonday);
}
orderdao.xml
<?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.ybb.dao.OrderDao">
<insert id="add" parameterType="map">
insert into t_order (member_id,orderDate,orderType,orderStatus,setmeal_id)
values
(#{member_id},#{orderDate},#{orderType},#{orderStatus},#{setmeal_id})
</insert>
<select id="findById4Detail" parameterType="int" resultType="map">
select m.name member ,s.name setmeal,o.orderDate orderDate,o.orderType orderType from
t_order o, t_member m, t_setmeal s
where o.member_id=m.id and o.setmeal_id=s.id and o.member_id=#{id}
</select>
<select id="findByCondition" resultType="com.ybb.pojo.Member">
select * from t_order where id=#{id} and orderDate =#{orderDate} and setmeal_id =#{setmeal_id}
</select>
<select id="findHotSetmeal" resultType="java.util.Map">
select s.name,count(o.id) setmeal_count, count(o.id)/(select count(id) from t_order)*100 proportion
from t_order o,t_setmeal s where
o.setmeal_id=s.id
group by o.setmeal_id
order by setmeal_count desc
limit 3
</select>
<!--今日预约-->
<select id="findOrderCountByDate" resultType="java.lang.Integer" parameterType="string">
select count(id) from t_order where orderDate=#{today}
</select>
<!--今日预约到诊-->
<select id="findVisitsCountByDate" resultType="java.lang.Integer" parameterType="string">
select count(id) from t_order where orderDate=#{today} and orderStatus='已到诊'
</select>
<!--本周预约数-->
<select id="findOrderCountAfterDate" resultType="java.lang.Integer" parameterType="string">
select count(id) from t_order where orderDate >=#{thisweekMonday}
</select>
<select id="findVisitsCountAfterDate" resultType="java.lang.Integer" parameterType="string">
select count(id) from t_order where orderDate >=#{today} and orderStatus='已到诊'
</select>
</mapper>
重点在套餐查询,需要分组,排序和分页,然后查出来后还需要除总的,起别名。
3. 运营数据统计报表导出
3.1 需求分析
运营数据统计报表导出就是将统计数据写入到Excel并提供给客户端浏览器进行下载,以便体检机构管
理人员对运营数据的查看和存档。
3.2 提供模板文件
通过上面的Excel效果可以看到,表格比较复杂,涉及到合并单元格、字体、字号、字体加粗、对齐方
式等的设置。如果我们通过POI编程的方式来设置这些效果代码会非常繁琐。
在企业实际开发中,对于这种比较复杂的表格导出一般我们会提前设计一个Excel模板文件,在这个模
板文件中提前将表格的结构和样式设置好,我们的程序只需要读取这个文件并在文件中的相应位置写入
具体的值就可以了。
在本章节资料中已经提供了一个名为report_template.xlsx的模板文件,需要将这个文件复制到
health_backend工程的template目录中
3.3 完善页面
<div class="excelTitle" >
<el-button @click="exportExcel">导出Excel</el-button>运营数据统计
</div>
methods:{
exportExcel(){
window.location.href = '/report/exportBusinessReport.do';
}
}
3.4 后台代码
在ReportController中提供exportBusinessReport方法,基于POI将数据写入到Excel中并通过输出流下
载到客户端
@RequestMapping("/exportBusinessReport")
public Result exportBusinessReport(HttpServletRequest request, HttpServletResponse response){
try {
Map<String, Object> data = reportService.getBusinessReportData();
String reportData = (String) data.get("reportDate");
Integer todayNewMember = (Integer)data.get("todayNewMember");
Integer totalMember = (Integer) data.get("totalMember");
Integer thisWeekNewMember = (Integer) data.get("thisWeekNewMember");
Integer thisMonthNewMember = (Integer) data.get("thisMonthNewMember");
Integer todayOrderNumber = (Integer) data.get("todayOrderNumber");
Integer todayVisitfindMemberCountBeforeDatesNumber = (Integer) data.get("todayVisitfindMemberCountBeforeDatesNumber");
Integer thisWeekOrderNumber = (Integer) data.get("thisWeekOrderNumber");
Integer thisWeekVisitsNumber = (Integer) data.get("thisWeekVisitsNumber");
Integer thisMonthOrderNumber = (Integer) data.get("thisMonthOrderNumber");
Integer thisMonthVisitsNumber = (Integer) data.get("thisMonthVisitsNumber");
List<Map> hotSetmeal = (List<Map>) data.get("hotSetmeal");
//动态的获得该文件所在的磁盘路径, file.separator自动适应操作系统分隔符
String filePath = request.getSession().getServletContext().getRealPath("template") + File.separator + "report_template.xlsx";
XSSFWorkbook excel=new XSSFWorkbook(new File(filePath));
//读取第一个工作表
XSSFSheet sheet = excel.getSheetAt(0);
//日期
XSSFRow row = sheet.getRow(2);
row.getCell(5).setCellValue(reportData);
//新增会员
row = sheet.getRow(4);
row.getCell(5).setCellValue(todayNewMember);
row.getCell(7).setCellValue(totalMember);
row=sheet.getRow(5);
row.getCell(5).setCellValue(thisWeekNewMember);
row.getCell(7).setCellValue(thisMonthNewMember);
row=sheet.getRow(7);
row.getCell(5).setCellValue(todayOrderNumber);
row.getCell(7).setCellValue(todayVisitfindMemberCountBeforeDatesNumber);
row=sheet.getRow(8);
row.getCell(5).setCellValue(thisWeekOrderNumber);
row.getCell(7).setCellValue(thisWeekVisitsNumber);
row=sheet.getRow(9);
row.getCell(5).setCellValue(thisMonthOrderNumber);
row.getCell(7).setCellValue(thisMonthVisitsNumber);
int rowNum=12;
for (Map map : hotSetmeal) {
row=sheet.getRow(rowNum++);
BigDecimal proportion = (BigDecimal)map.get("proportion");
row.getCell(4).setCellValue((String) map.get("name"));
row.getCell(5).setCellValue((long) map.get("setmeal_count"));
row.getCell(6).setCellValue(proportion.doubleValue());
}
ServletOutputStream out = response.getOutputStream();
response.setContentType("application/vnd.ms-execl");//execl文件类型
response.setHeader("content-Disposition","attachment;filename=report.xlsx");//指定以附件下载
excel.write(out);
out.flush();
out.close();
excel.close();
return null;
}catch (Exception e){
return new Result(false,MessageConstant.GET_MENU_FAIL);
}
}
同步请求喔。
这个主要是繁琐,一个一个表的位置插入之前查询的数据,execl表0,0开始的,写入的时候注意位置,写完后注意设置响应格式,然后关流。
权限控制菜单动态展示
分析
-
根据传入的user id查中间表和角色表,拿到角色表的id,然后根据role id查中间表和menu表。
-
menu表示关键。因为有子菜单,这里用java代码简化查询,第一次查询的时候根据role的id拿到父菜单,条件记得加and
level=1.出来的是集合,遍历集合,根据父菜单的id和parentMenuld查询子菜单,这里有一个点,每次遍历记得new一个,不然引用对象会有问题。 -
数据模型是数组对象,然后对象里套了个数组,这里用map封装
[ {x:y,xx:yy,xxx:{ } },{x:y,xx:yy,xxx:{ } }]
根据
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>传智健康</title>
<meta name="description" content="传智健康">
<meta name="keywords" content="传智健康">
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min(1).css">
<link rel="stylesheet" href="../css/style.css">
<style type="text/css">
.el-main {
position: absolute;
top: 70px;
bottom: 0px;
left: 200px;
right: 10px;
padding: 0;
}
</style>
<script type="text/javascript" charset="utf-8" src="../L2Dwidget/L2Dwidget.0.min.js"></script>
<script type="text/javascript" charset="utf-8" src="../L2Dwidget/L2Dwidget.min.js"></script>
<script type="text/javascript">
L2Dwidget.init({
"display": {
"superSample": 3,
"width": 200,
"height": 400,
"position": "right",
"hOffset": 0,
"vOffset": 0
}
});
</script>
<style>
#live2dcanvas {
border: 0 !important;
}
</style>
</head>
<body class="hold-transition skin-purple sidebar-mini">
<div id="app">
<el-container>
<el-header class="main-header" style="height:70px;">
<nav class="navbar navbar-static-top" :class=''>
<!-- Logo -->
<a href="#" class="logo" style="text-align:center">
<span class="logo-lg"><img src="../img/logo.png"></span>
</a>
<!--autoplay 搞完吧auto加入-->
<audio id="audio" src="../1551961057/acb.mp3" controls loop controlsList="nodownload"
oncontextmenu="return false">
</audio>
<div class="right-menu">
<span class="help"><i class="fa fa-exclamation-circle" aria-hidden="true"></i>帮助</span>
<el-dropdown class="avatar-container right-menu-item" trigger="click">
<div class="avatar-wrapper">
<img src="../img/user2-160x160.jpg" class="user-avatar">
{{username}}
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item divided>
<span style="display:block;">修改密码</span>
</el-dropdown-item>
<el-dropdown-item divided>
<span style="display:block;">
<a href="/logout.do">退出</a>
</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</nav>
</el-header>
<el-container>
<el-aside width="200px">
<el-menu>
<el-submenu v-for="menu in menuList" :index="menu.path">
<template slot="title">
<i class="fa" :class="menu.icon"></i>
{{menu.title}}
</template>
<template v-for="child in menu.children">
<el-menu-item :index="child.path">
<a :href="child.linkUrl" target="right">{{child.title}}</a>
</el-menu-item>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<iframe name="right" class="el-main" src="../1551961057/bb.html" width="100%" height="750px"
frameborder="0"></iframe>
</el-container>
</el-container>
</el-container>
</div>
</body>
<!-- 引入组件库 -->
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
new Vue({
el: '#app',
data: {
username: null,
menuList: [
{
"path": "1",
"title": "工作台",
"icon": "fa-dashboard",
"children": []
},
{
"path": "2",
"title": "会员管理",
"icon": "fa-user-md",
"children": [
{
"path": "/2-1",
"title": "会员档案",
"linkUrl": "member.html",
"children": []
},
{
"path": "/2-2",
"title": "体检上传",
"children": []
},
{
"path": "/2-3",
"title": "会员统计",
"linkUrl": "all-item-list.html",
"children": []
},
]
},
{
"path": "3",
"title": "预约管理",
"icon": "fa-tty",
"children": [
{
"path": "/3-1",
"title": "预约列表",
"linkUrl": "ordersettinglist.html",
"children": []
},
{
"path": "/3-2",
"title": "预约设置",
"linkUrl": "ordersetting.html",
"children": []
},
{
"path": "/3-3",
"title": "套餐管理",
"linkUrl": "setmeal.html",
"children": []
},
{
"path": "/3-4",
"title": "检查组管理",
"linkUrl": "checkgroup.html",
"children": []
},
{
"path": "/3-5",
"title": "检查项管理",
"linkUrl": "checkitem.html",
"children": []
},
]
},
{
"path": "4",
"title": "健康评估",
"icon": "fa-stethoscope",
"children": [
{
"path": "/4-1",
"title": "中医体质辨识",
"linkUrl": "all-medical-list.html",
"children": []
},
]
},
{
"path": "5", //菜单项所对应的路由路径
"title": "统计分析", //菜单项名称
"icon": "fa-heartbeat",
"children": [//是否有子菜单,若没有,则为[]
{
"path": "/5-1",
"title": "会员数量统计",
"linkUrl": "report_member.html",
"children": []
},
{
"path": "/5-2",
"title": "套餐预约占比统计",
"linkUrl": "report_setmeal.html",
"children": []
},
{
"path": "/5-3",
"title": "运营数据统计",
"linkUrl": "report_business.html",
"children": []
}
]
}
]
},
created(){
axios.get("/user/getUsername.do").then((res)=>{
if (res.data.flag){
this.username=res.data.data.username
}
});
axios.get("/user/getMenus.do").then((res)=>{
if (res.data.flag){
this.menuList=res.data.data
console.log(this.menuList);
}
});
},
});
$(function () {
var wd = 200;
$(".el-main").css('width', $('body').width() - wd + 'px');
});
</script>
</html>
controller
@RequestMapping("/getMenus")
public Result getMenus(){
Object user = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println(user);
if (user!=null){
com.ybb.pojo.User user1 = userService.findByUserName(((User)user).getUsername());
List<Map<String,Object>>maps = userService.findUserMenu(user1.getId());
return new Result(true, MessageConstant.GET_MENU_SUCCESS,maps);
}
return new Result(false,MessageConstant.GET_MENU_FAIL);
}
service
要根据前面username来查数据库,拿到id,就可以操作了
public interface UserService {
User findByUserName(String username);
//根据用户id查询到套餐数据
List<Map<String,Object>> findUserMenu(Integer id);
}
serviceImpl
package com.ybb.service.Impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ybb.dao.MenuDao;
import com.ybb.dao.PermissionDao;
import com.ybb.dao.RoleDao;
import com.ybb.dao.UserDao;
import com.ybb.pojo.Menu;
import com.ybb.pojo.Permission;
import com.ybb.pojo.Role;
import com.ybb.pojo.User;
import com.ybb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* Description :
* Version :1.0
*/
@Service(interfaceClass = UserService.class)
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
@Autowired
private PermissionDao permissionDao;
//根据用户名查询书库获取用户信息和关联的角色信息,同时需要查询角色关联的权限信息
@Override
public User findByUserName(String username) {
User user = userDao.findByUserName(username);
if (user==null){
return null;
}
Integer userId = user.getId();
//根据用户ID查询对应的角色
Set<Role> roles = roleDao.findByUserId(userId);
for (Role role : roles) {
//角色ID,去查询关联权限
Integer roleId = role.getId();
Set<Permission> permissions = permissionDao.findByRoleId(roleId);
role.setPermissions(permissions);
}
user.setRoles(roles);
return user;
}
@Autowired
private MenuDao menuDao;
@Override
public List<Map<String,Object>> findUserMenu(Integer id) {
//根据用户传入的id查询中间表,找到对应的角色id
Role role = roleDao.findByRole(id);
//先根据父菜单找到子菜单,再根据父菜单的id拿到子菜单,放入父菜单的属性中
List<Map<String, Object>> maps = menuDao.parentMenus(role.getId());
for (Map<String, Object> map : maps) {
Integer parentId = (Integer) map.get("id");
List<Map<String,String>> sonmaps =new ArrayList<>();
sonmaps= menuDao.sonMenus(parentId);
map.put("children",sonmaps);
}
return maps;
}
}
permissiondao
```java
public interface PermissionDao {
Set<Permission> findByRoleId(Integer RoleId);
}
permissiondao.xml
<?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.ybb.dao.PermissionDao">
<select id="findByRoleId" resultType="com.ybb.pojo.Permission" parameterType="int">
SELECT p.* FROM t_permission p,t_role_permission rp
where p.id=rp.permission_id and rp.role_id=#{RoleId}
</select>
</mapper>
menuDao
```java
public interface MenuDao {
List<Map<String,Object>> parentMenus(Integer id);
List<Map<String,String>>sonMenus(Integer id);
}
menuDao.xml
<?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.ybb.dao.MenuDao">
<select id="parentMenus" resultType="java.util.Map" parameterType="int">
SELECT tm.id id,tm.path path,tm.name title,tm.icon icon
FROM t_role_menu trm,t_menu tm
where trm.menu_id=tm.id and trm.role_id=#{id} and tm.level=1
</select>
<select id="sonMenus" resultType="java.util.Map" parameterType="int">
SELECT t2.path path,t2.name title,t2.linkUrl linkUrl
FROM t_menu t1,t_menu t2
where t1.id=t2.parentMenuId and t1.id=#{id};
</select>
</mapper>