feat: add dashboard enhancement with ECharts charts (#20)
- Add 6 dashboard query methods to RlzOrderMapper (status/type/trend/ranking) - Add dashboard endpoint /system/view/dashboard combining all stats - Rewrite index.vue with ECharts: summary cards, order/revenue trend, status pie, hospital and caregiver ranking charts - Add getDashboard API to finance.js Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -69,3 +69,12 @@ export function getJiaoyiDetail(id) {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 仪表盘 ==========
|
||||||
|
|
||||||
|
export function getDashboard() {
|
||||||
|
return request({
|
||||||
|
url: '/system/view/dashboard',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,121 +1,251 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container home">
|
<div class="app-container home">
|
||||||
|
<!-- 统计卡片 -->
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20" class="mb20">
|
||||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
<el-col :xs="24" :sm="12" :lg="6">
|
||||||
<h2>瑞来兹医助后台管理框架</h2>
|
<div class="stat-card card-total">
|
||||||
<p>
|
<div class="stat-icon"><i class="el-icon-document" /></div>
|
||||||
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了瑞来兹医助管理系统,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
|
<div class="stat-body">
|
||||||
</p>
|
<div class="stat-value">{{ summary.totalOrders }}</div>
|
||||||
<p>
|
<div class="stat-label">订单总数</div>
|
||||||
<b>当前版本:</b> <span>v{{ version }}</span>
|
</div>
|
||||||
</p>
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="6">
|
||||||
|
<div class="stat-card card-today">
|
||||||
|
<div class="stat-icon"><i class="el-icon-date" /></div>
|
||||||
|
<div class="stat-body">
|
||||||
|
<div class="stat-value">{{ summary.todayOrders }}</div>
|
||||||
|
<div class="stat-label">今日订单</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="6">
|
||||||
|
<div class="stat-card card-revenue">
|
||||||
|
<div class="stat-icon"><i class="el-icon-money" /></div>
|
||||||
|
<div class="stat-body">
|
||||||
|
<div class="stat-value">{{ summary.totalRevenue }}</div>
|
||||||
|
<div class="stat-label">总收入(元)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="6">
|
||||||
|
<div class="stat-card card-pending">
|
||||||
|
<div class="stat-icon"><i class="el-icon-bell" /></div>
|
||||||
|
<div class="stat-body">
|
||||||
|
<div class="stat-value">{{ summary.pendingOrders }}</div>
|
||||||
|
<div class="stat-label">待处理订单</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-divider />
|
|
||||||
<!-- <div class="iframe-out" >
|
|
||||||
<iframe
|
|
||||||
id="ifr"
|
|
||||||
:src="src"
|
|
||||||
ref="iframe"
|
|
||||||
style="min-height: 60vh; width: 100%"
|
|
||||||
marginheight="0"
|
|
||||||
marginwidth="0"
|
|
||||||
frameborder="0"
|
|
||||||
scrolling="auto"
|
|
||||||
></iframe>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<!-- 第一行图表:订单趋势 + 状态分布 -->
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="16">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header"><span>近30天订单/收入趋势</span></div>
|
||||||
|
<div ref="orderTrendChart" style="height:350px" v-loading="loading" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header"><span>订单状态分布</span></div>
|
||||||
|
<div ref="statusPieChart" style="height:350px" v-loading="loading" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 第二行图表:医院排名 + 陪护排名 -->
|
||||||
|
<el-row :gutter="20" style="margin-top:20px">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header"><span>医院服务量排名 (TOP10)</span></div>
|
||||||
|
<div ref="hospitalChart" style="height:350px" v-loading="loading" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header"><span>陪护师服务排名 (TOP10)</span></div>
|
||||||
|
<div ref="caregiverChart" style="height:350px" v-loading="loading" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getAuthToken } from "@/api/system/user";
|
import echarts from 'echarts';
|
||||||
|
import { getDashboard } from "@/api/system/finance";
|
||||||
|
|
||||||
|
const STATUS_MAP = {
|
||||||
|
'-2': '已取消', '-1': '拒绝接单', '0': '待接单', '1': '已接单',
|
||||||
|
'2': '已支付', '3': '服务中', '4': '已完成',
|
||||||
|
'5': '申请退款', '6': '退款中', '7': '已退款', '8': '已结算'
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Index",
|
name: "Index",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 版本号
|
|
||||||
version: "3.8.3",
|
version: "3.8.3",
|
||||||
src: ""
|
loading: false,
|
||||||
|
dashboard: {},
|
||||||
|
summary: { totalOrders: 0, todayOrders: 0, totalRevenue: 0, pendingOrders: 0 }
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
mounted() {
|
||||||
// getAuthToken().then((response=>{
|
this.loadDashboard();
|
||||||
// let tokens= response.authToken;
|
|
||||||
// this.src="http://124.70.96.132:8000/ztbPortal/rest/companyLogin/loginUser1?"+tokens;
|
|
||||||
// alert(this.src);
|
|
||||||
// }))
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goTarget(href) {
|
loadDashboard() {
|
||||||
window.open(href, "_blank");
|
this.loading = true;
|
||||||
|
getDashboard().then(response => {
|
||||||
|
this.dashboard = response.data || response;
|
||||||
|
this.calcSummary();
|
||||||
|
this.loading = false;
|
||||||
|
this.$nextTick(() => { this.renderAllCharts(); });
|
||||||
|
}).catch(() => { this.loading = false; });
|
||||||
},
|
},
|
||||||
|
calcSummary() {
|
||||||
|
const d = this.dashboard;
|
||||||
|
// 订单总数
|
||||||
|
const statusStats = d.orderStatusStats || [];
|
||||||
|
this.summary.totalOrders = statusStats.reduce((s, r) => s + parseInt(r.cnt || 0), 0);
|
||||||
|
// 今日订单
|
||||||
|
const trend = d.dailyOrderTrend || [];
|
||||||
|
const today = new Date().toISOString().slice(0, 10);
|
||||||
|
const todayItem = trend.find(r => (r.date || '').slice(0, 10) === today);
|
||||||
|
this.summary.todayOrders = todayItem ? parseInt(todayItem.cnt || 0) : 0;
|
||||||
|
// 总收入
|
||||||
|
const revTrend = d.dailyRevenueTrend || [];
|
||||||
|
this.summary.totalRevenue = revTrend.reduce((s, r) => s + parseFloat(r.revenue || 0), 0).toFixed(2);
|
||||||
|
// 待处理: 待接单 + 申请退款
|
||||||
|
const pending = statusStats.filter(r => ['0', '5'].includes(r.status));
|
||||||
|
this.summary.pendingOrders = pending.reduce((s, r) => s + parseInt(r.cnt || 0), 0);
|
||||||
},
|
},
|
||||||
|
renderAllCharts() {
|
||||||
|
this.renderOrderTrendChart();
|
||||||
|
this.renderStatusPieChart();
|
||||||
|
this.renderHospitalChart();
|
||||||
|
this.renderCaregiverChart();
|
||||||
|
},
|
||||||
|
renderOrderTrendChart() {
|
||||||
|
const el = this.$refs.orderTrendChart;
|
||||||
|
if (!el) return;
|
||||||
|
const chart = echarts.init(el);
|
||||||
|
const orderTrend = this.dashboard.dailyOrderTrend || [];
|
||||||
|
const revenueTrend = this.dashboard.dailyRevenueTrend || [];
|
||||||
|
const dates = orderTrend.map(r => (r.date || '').slice(5));
|
||||||
|
const orders = orderTrend.map(r => parseInt(r.cnt || 0));
|
||||||
|
const revenues = orderTrend.map(r => {
|
||||||
|
const rev = revenueTrend.find(v => v.date === r.date);
|
||||||
|
return rev ? parseFloat(rev.revenue || 0) : 0;
|
||||||
|
});
|
||||||
|
chart.setOption({
|
||||||
|
tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },
|
||||||
|
legend: { data: ['订单数', '收入(元)'] },
|
||||||
|
grid: { left: 50, right: 50, bottom: 30, top: 30 },
|
||||||
|
xAxis: { type: 'category', data: dates },
|
||||||
|
yAxis: [
|
||||||
|
{ type: 'value', name: '订单数' },
|
||||||
|
{ type: 'value', name: '收入(元)' }
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{ name: '订单数', type: 'bar', data: orders, itemStyle: { color: '#409EFF' } },
|
||||||
|
{ name: '收入(元)', type: 'line', yAxisIndex: 1, data: revenues, smooth: true, itemStyle: { color: '#67C23A' } }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
window.addEventListener('resize', () => chart.resize());
|
||||||
|
},
|
||||||
|
renderStatusPieChart() {
|
||||||
|
const el = this.$refs.statusPieChart;
|
||||||
|
if (!el) return;
|
||||||
|
const chart = echarts.init(el);
|
||||||
|
const data = (this.dashboard.orderStatusStats || []).map(r => ({
|
||||||
|
name: STATUS_MAP[r.status] || ('状态' + r.status),
|
||||||
|
value: parseInt(r.cnt || 0)
|
||||||
|
}));
|
||||||
|
chart.setOption({
|
||||||
|
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
itemStyle: { borderRadius: 4, borderColor: '#fff', borderWidth: 2 },
|
||||||
|
label: { show: true, formatter: '{b}\n{d}%' },
|
||||||
|
data: data
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
window.addEventListener('resize', () => chart.resize());
|
||||||
|
},
|
||||||
|
renderHospitalChart() {
|
||||||
|
const el = this.$refs.hospitalChart;
|
||||||
|
if (!el) return;
|
||||||
|
const chart = echarts.init(el);
|
||||||
|
const data = this.dashboard.hospitalRanking || [];
|
||||||
|
chart.setOption({
|
||||||
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||||
|
grid: { left: 100, right: 50, bottom: 30, top: 10 },
|
||||||
|
xAxis: { type: 'value' },
|
||||||
|
yAxis: { type: 'category', data: data.map(r => r.name).reverse(), inverse: true,
|
||||||
|
axisLabel: { width: 90, overflow: 'truncate' } },
|
||||||
|
series: [{
|
||||||
|
type: 'bar',
|
||||||
|
data: data.map(r => parseInt(r.cnt || 0)).reverse(),
|
||||||
|
itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: '#409EFF' }, { offset: 1, color: '#67C23A' }
|
||||||
|
])}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
window.addEventListener('resize', () => chart.resize());
|
||||||
|
},
|
||||||
|
renderCaregiverChart() {
|
||||||
|
const el = this.$refs.caregiverChart;
|
||||||
|
if (!el) return;
|
||||||
|
const chart = echarts.init(el);
|
||||||
|
const data = this.dashboard.caregiverRanking || [];
|
||||||
|
chart.setOption({
|
||||||
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||||
|
grid: { left: 100, right: 50, bottom: 30, top: 10 },
|
||||||
|
xAxis: { type: 'value' },
|
||||||
|
yAxis: { type: 'category', data: data.map(r => r.name).reverse(), inverse: true,
|
||||||
|
axisLabel: { width: 90, overflow: 'truncate' } },
|
||||||
|
series: [{
|
||||||
|
type: 'bar',
|
||||||
|
data: data.map(r => parseInt(r.cnt || 0)).reverse(),
|
||||||
|
itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: '#E6A23C' }, { offset: 1, color: '#F56C6C' }
|
||||||
|
])}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
window.addEventListener('resize', () => chart.resize());
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.home {
|
.home {
|
||||||
blockquote {
|
.mb20 { margin-bottom: 20px; }
|
||||||
padding: 10px 20px;
|
|
||||||
margin: 0 0 20px;
|
|
||||||
font-size: 17.5px;
|
|
||||||
border-left: 5px solid #eee;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border: 0;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
.col-item {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
.stat-card {
|
||||||
padding: 0;
|
display: flex; align-items: center; padding: 20px; border-radius: 8px;
|
||||||
margin: 0;
|
color: #fff; overflow: hidden; position: relative;
|
||||||
|
.stat-icon {
|
||||||
|
font-size: 48px; opacity: 0.3; margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
.stat-body {
|
||||||
|
.stat-value { font-size: 28px; font-weight: bold; line-height: 1.2; }
|
||||||
|
.stat-label { font-size: 13px; margin-top: 4px; opacity: 0.85; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-total { background: linear-gradient(135deg, #667eea, #764ba2); }
|
||||||
|
.card-today { background: linear-gradient(135deg, #43e97b, #38f9d7); }
|
||||||
|
.card-revenue { background: linear-gradient(135deg, #f093fb, #f5576c); }
|
||||||
|
.card-pending { background: linear-gradient(135deg, #fa709a, #fee140); }
|
||||||
|
|
||||||
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eee; }
|
||||||
font-size: 13px;
|
h2 { margin-top: 10px; font-size: 26px; font-weight: 100; }
|
||||||
color: #676a6c;
|
p { margin-top: 10px; b { font-weight: 700; } }
|
||||||
overflow-x: hidden;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 26px;
|
|
||||||
font-weight: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
b {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.update-log {
|
|
||||||
ol {
|
|
||||||
display: block;
|
|
||||||
list-style-type: decimal;
|
|
||||||
margin-block-start: 1em;
|
|
||||||
margin-block-end: 1em;
|
|
||||||
margin-inline-start: 0;
|
|
||||||
margin-inline-end: 0;
|
|
||||||
padding-inline-start: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,22 @@ public class OrderViewController extends BaseController
|
|||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘统计数据
|
||||||
|
*/
|
||||||
|
@GetMapping("/dashboard")
|
||||||
|
public AjaxResult dashboard()
|
||||||
|
{
|
||||||
|
java.util.Map<String, Object> result = new java.util.HashMap<>();
|
||||||
|
result.put("orderStatusStats", iRlzOrderService.getOrderStatusStats());
|
||||||
|
result.put("orderTypeStats", iRlzOrderService.getOrderTypeStats());
|
||||||
|
result.put("dailyOrderTrend", iRlzOrderService.getDailyOrderTrend());
|
||||||
|
result.put("dailyRevenueTrend", iRlzOrderService.getDailyRevenueTrend());
|
||||||
|
result.put("hospitalRanking", iRlzOrderService.getHospitalRanking());
|
||||||
|
result.put("caregiverRanking", iRlzOrderService.getCaregiverRanking());
|
||||||
|
return AjaxResult.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取VIEW详细信息
|
* 获取VIEW详细信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -63,4 +63,34 @@ public interface RlzOrderMapper
|
|||||||
* 收入统计
|
* 收入统计
|
||||||
*/
|
*/
|
||||||
public List<java.util.Map<String, Object>> getIncomeStats(java.util.Map<String, Object> params);
|
public List<java.util.Map<String, Object>> getIncomeStats(java.util.Map<String, Object> params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-订单状态分布
|
||||||
|
*/
|
||||||
|
public List<java.util.Map<String, Object>> getOrderStatusStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-服务类型分布
|
||||||
|
*/
|
||||||
|
public List<java.util.Map<String, Object>> getOrderTypeStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-日订单趋势(近30天)
|
||||||
|
*/
|
||||||
|
public List<java.util.Map<String, Object>> getDailyOrderTrend();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-日收入趋势(近30天)
|
||||||
|
*/
|
||||||
|
public List<java.util.Map<String, Object>> getDailyRevenueTrend();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-医院服务量排名
|
||||||
|
*/
|
||||||
|
public List<java.util.Map<String, Object>> getHospitalRanking();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-陪护服务排名
|
||||||
|
*/
|
||||||
|
public List<java.util.Map<String, Object>> getCaregiverRanking();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,4 +78,34 @@ public interface IRlzOrderService
|
|||||||
Long insertOrderPz(RlzOrder rlzOrder);
|
Long insertOrderPz(RlzOrder rlzOrder);
|
||||||
|
|
||||||
List<java.util.Map<String, Object>> getIncomeStats(java.util.Map<String, Object> params);
|
List<java.util.Map<String, Object>> getIncomeStats(java.util.Map<String, Object> params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-订单状态分布
|
||||||
|
*/
|
||||||
|
List<java.util.Map<String, Object>> getOrderStatusStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-服务类型分布
|
||||||
|
*/
|
||||||
|
List<java.util.Map<String, Object>> getOrderTypeStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-日订单趋势(近30天)
|
||||||
|
*/
|
||||||
|
List<java.util.Map<String, Object>> getDailyOrderTrend();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-日收入趋势(近30天)
|
||||||
|
*/
|
||||||
|
List<java.util.Map<String, Object>> getDailyRevenueTrend();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-医院服务量排名
|
||||||
|
*/
|
||||||
|
List<java.util.Map<String, Object>> getHospitalRanking();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘-陪护服务排名
|
||||||
|
*/
|
||||||
|
List<java.util.Map<String, Object>> getCaregiverRanking();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -349,4 +349,34 @@ public String weixinPayOrderNext(String decryptOrder) {
|
|||||||
public List<java.util.Map<String, Object>> getIncomeStats(java.util.Map<String, Object> params) {
|
public List<java.util.Map<String, Object>> getIncomeStats(java.util.Map<String, Object> params) {
|
||||||
return rlzOrderMapper.getIncomeStats(params);
|
return rlzOrderMapper.getIncomeStats(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<java.util.Map<String, Object>> getOrderStatusStats() {
|
||||||
|
return rlzOrderMapper.getOrderStatusStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<java.util.Map<String, Object>> getOrderTypeStats() {
|
||||||
|
return rlzOrderMapper.getOrderTypeStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<java.util.Map<String, Object>> getDailyOrderTrend() {
|
||||||
|
return rlzOrderMapper.getDailyOrderTrend();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<java.util.Map<String, Object>> getDailyRevenueTrend() {
|
||||||
|
return rlzOrderMapper.getDailyRevenueTrend();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<java.util.Map<String, Object>> getHospitalRanking() {
|
||||||
|
return rlzOrderMapper.getHospitalRanking();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<java.util.Map<String, Object>> getCaregiverRanking() {
|
||||||
|
return rlzOrderMapper.getCaregiverRanking();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,6 +244,41 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<select id="getOrderStatusStats" resultType="map">
|
||||||
|
SELECT status, COUNT(*) as cnt FROM rlz_order GROUP BY status ORDER BY status
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getOrderTypeStats" resultType="map">
|
||||||
|
SELECT COALESCE(NULLIF(yuliu10,''), '其他') as name, COUNT(*) as cnt
|
||||||
|
FROM rlz_order GROUP BY yuliu10 ORDER BY cnt DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getDailyOrderTrend" resultType="map">
|
||||||
|
SELECT DATE(create_time) as date, COUNT(*) as cnt
|
||||||
|
FROM rlz_order WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||||||
|
GROUP BY DATE(create_time) ORDER BY date
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getDailyRevenueTrend" resultType="map">
|
||||||
|
SELECT DATE(create_time) as date,
|
||||||
|
COALESCE(SUM(CASE WHEN status IN ('4','8') THEN CAST(COALESCE(NULLIF(jiesuan_money,''), yugu_money) AS DECIMAL(10,2)) ELSE 0 END), 0) as revenue
|
||||||
|
FROM rlz_order WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||||||
|
GROUP BY DATE(create_time) ORDER BY date
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getHospitalRanking" resultType="map">
|
||||||
|
SELECT COALESCE(h.hospitalname, '未指定') as name, COUNT(*) as cnt
|
||||||
|
FROM rlz_order o LEFT JOIN sys_hospital h ON o.hospital_id = h.id
|
||||||
|
GROUP BY o.hospital_id, h.hospitalname ORDER BY cnt DESC LIMIT 10
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getCaregiverRanking" resultType="map">
|
||||||
|
SELECT COALESCE(ub.nick_name, CONCAT('用户#', o.b_id)) as name, COUNT(*) as cnt
|
||||||
|
FROM rlz_order o LEFT JOIN sys_user ub ON o.b_id = ub.user_id
|
||||||
|
WHERE o.b_id IS NOT NULL
|
||||||
|
GROUP BY o.b_id, ub.nick_name ORDER BY cnt DESC LIMIT 10
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="getIncomeStats" parameterType="map" resultType="map">
|
<select id="getIncomeStats" parameterType="map" resultType="map">
|
||||||
SELECT
|
SELECT
|
||||||
<choose>
|
<choose>
|
||||||
|
|||||||
Reference in New Issue
Block a user