feat(reportmanage): T11.2 经营分析+数据导出 - AppService + Controller + Frontend
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
package com.healthlink.his.web.reportmanage.appservice;
|
||||||
|
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IBusinessAnalyticsAppService {
|
||||||
|
R<?> generateReport(Map<String, Object> params);
|
||||||
|
R<?> exportToExcel(Map<String, Object> params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package com.healthlink.his.web.reportmanage.appservice.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.healthlink.his.quality.domain.BusinessAnalytics;
|
||||||
|
import com.healthlink.his.quality.service.IBusinessAnalyticsService;
|
||||||
|
import com.healthlink.his.web.reportmanage.appservice.IBusinessAnalyticsAppService;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class BusinessAnalyticsAppServiceImpl implements IBusinessAnalyticsAppService {
|
||||||
|
|
||||||
|
private final IBusinessAnalyticsService analyticsService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<?> generateReport(Map<String, Object> params) {
|
||||||
|
String departmentName = (String) params.get("departmentName");
|
||||||
|
String startDate = (String) params.get("startDate");
|
||||||
|
String endDate = (String) params.get("endDate");
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BusinessAnalytics> w = new LambdaQueryWrapper<>();
|
||||||
|
w.eq(StringUtils.hasText(departmentName), BusinessAnalytics::getDepartmentName, departmentName);
|
||||||
|
if (StringUtils.hasText(startDate)) {
|
||||||
|
w.ge(BusinessAnalytics::getStatDate, startDate);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(endDate)) {
|
||||||
|
w.le(BusinessAnalytics::getStatDate, endDate);
|
||||||
|
}
|
||||||
|
w.orderByDesc(BusinessAnalytics::getStatDate);
|
||||||
|
List<BusinessAnalytics> list = analyticsService.list(w);
|
||||||
|
|
||||||
|
Map<String, Object> report = new HashMap<>();
|
||||||
|
report.put("totalRecords", list.size());
|
||||||
|
|
||||||
|
BigDecimal totalRevenue = BigDecimal.ZERO;
|
||||||
|
BigDecimal totalCost = BigDecimal.ZERO;
|
||||||
|
int totalPatients = 0;
|
||||||
|
int totalBeds = 0;
|
||||||
|
for (BusinessAnalytics ba : list) {
|
||||||
|
if (ba.getRevenue() != null) totalRevenue = totalRevenue.add(ba.getRevenue());
|
||||||
|
if (ba.getCost() != null) totalCost = totalCost.add(ba.getCost());
|
||||||
|
if (ba.getPatientCount() != null) totalPatients += ba.getPatientCount();
|
||||||
|
if (ba.getBedCount() != null) totalBeds += ba.getBedCount();
|
||||||
|
}
|
||||||
|
report.put("totalRevenue", totalRevenue);
|
||||||
|
report.put("totalCost", totalCost);
|
||||||
|
report.put("totalProfit", totalRevenue.subtract(totalCost));
|
||||||
|
report.put("totalPatients", totalPatients);
|
||||||
|
report.put("totalBeds", totalBeds);
|
||||||
|
int size = list.size();
|
||||||
|
report.put("avgRevenue", size > 0 ? totalRevenue.divide(BigDecimal.valueOf(size), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO);
|
||||||
|
report.put("avgCost", size > 0 ? totalCost.divide(BigDecimal.valueOf(size), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO);
|
||||||
|
report.put("profitRate", totalRevenue.compareTo(BigDecimal.ZERO) > 0
|
||||||
|
? totalRevenue.subtract(totalCost).multiply(BigDecimal.valueOf(100)).divide(totalRevenue, 2, RoundingMode.HALF_UP) : BigDecimal.ZERO);
|
||||||
|
|
||||||
|
Map<String, BigDecimal> deptRevenue = list.stream()
|
||||||
|
.filter(ba -> StringUtils.hasText(ba.getDepartmentName()))
|
||||||
|
.collect(Collectors.groupingBy(
|
||||||
|
BusinessAnalytics::getDepartmentName,
|
||||||
|
Collectors.reducing(BigDecimal.ZERO, ba -> ba.getRevenue() != null ? ba.getRevenue() : BigDecimal.ZERO, BigDecimal::add)));
|
||||||
|
report.put("departmentRevenue", deptRevenue);
|
||||||
|
|
||||||
|
report.put("records", list.stream().limit(50).collect(Collectors.toList()));
|
||||||
|
return R.ok(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<?> exportToExcel(Map<String, Object> params) {
|
||||||
|
String departmentName = (String) params.get("departmentName");
|
||||||
|
String startDate = (String) params.get("startDate");
|
||||||
|
String endDate = (String) params.get("endDate");
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BusinessAnalytics> w = new LambdaQueryWrapper<>();
|
||||||
|
w.eq(StringUtils.hasText(departmentName), BusinessAnalytics::getDepartmentName, departmentName);
|
||||||
|
if (StringUtils.hasText(startDate)) {
|
||||||
|
w.ge(BusinessAnalytics::getStatDate, startDate);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(endDate)) {
|
||||||
|
w.le(BusinessAnalytics::getStatDate, endDate);
|
||||||
|
}
|
||||||
|
w.orderByDesc(BusinessAnalytics::getStatDate);
|
||||||
|
List<BusinessAnalytics> list = analyticsService.list(w);
|
||||||
|
|
||||||
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
|
rows.add(List.of("日期", "科室", "收入(万元)", "成本(万元)", "利润(万元)", "患者数", "床位数", "床位率(%)", "平均住院日", "平均费用(万元)"));
|
||||||
|
for (BusinessAnalytics ba : list) {
|
||||||
|
rows.add(List.of(
|
||||||
|
ba.getStatDate() != null ? ba.getStatDate() : "",
|
||||||
|
ba.getDepartmentName() != null ? ba.getDepartmentName() : "",
|
||||||
|
ba.getRevenue() != null ? ba.getRevenue().divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO,
|
||||||
|
ba.getCost() != null ? ba.getCost().divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO,
|
||||||
|
ba.getProfit() != null ? ba.getProfit().divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO,
|
||||||
|
ba.getPatientCount() != null ? ba.getPatientCount() : 0,
|
||||||
|
ba.getBedCount() != null ? ba.getBedCount() : 0,
|
||||||
|
ba.getBedOccupancyRate() != null ? ba.getBedOccupancyRate() : BigDecimal.ZERO,
|
||||||
|
ba.getAvgStayDays() != null ? ba.getAvgStayDays() : BigDecimal.ZERO,
|
||||||
|
ba.getAvgCost() != null ? ba.getAvgCost().divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("headers", rows.get(0));
|
||||||
|
result.put("data", rows.subList(1, rows.size()));
|
||||||
|
result.put("totalRows", rows.size() - 1);
|
||||||
|
return R.ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.healthlink.his.web.reportmanage.controller;
|
||||||
|
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.healthlink.his.web.reportmanage.appservice.IBusinessAnalyticsAppService;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/report/analytics")
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class BusinessAnalyticsController {
|
||||||
|
|
||||||
|
private final IBusinessAnalyticsAppService analyticsAppService;
|
||||||
|
|
||||||
|
@PostMapping("/generate")
|
||||||
|
@PreAuthorize("hasAuthority('infection:report:edit')")
|
||||||
|
public R<?> generateReport(@RequestBody Map<String, Object> params) {
|
||||||
|
return analyticsAppService.generateReport(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export")
|
||||||
|
@PreAuthorize("hasAuthority('infection:report:list')")
|
||||||
|
public R<?> exportToExcel(
|
||||||
|
@RequestParam(required = false) String departmentName,
|
||||||
|
@RequestParam(required = false) String startDate,
|
||||||
|
@RequestParam(required = false) String endDate) {
|
||||||
|
Map<String, Object> params = new java.util.HashMap<>();
|
||||||
|
params.put("departmentName", departmentName);
|
||||||
|
params.put("startDate", startDate);
|
||||||
|
params.put("endDate", endDate);
|
||||||
|
return analyticsAppService.exportToExcel(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,3 +2,5 @@ import request from '@/utils/request'
|
|||||||
export function getAnalyticsPage(p){return request({url:'/business-analytics/page',method:'get',params:p})}
|
export function getAnalyticsPage(p){return request({url:'/business-analytics/page',method:'get',params:p})}
|
||||||
export function addAnalytics(d){return request({url:'/business-analytics/add',method:'post',data:d})}
|
export function addAnalytics(d){return request({url:'/business-analytics/add',method:'post',data:d})}
|
||||||
export function getAnalyticsSummary(){return request({url:'/business-analytics/summary',method:'get'})}
|
export function getAnalyticsSummary(){return request({url:'/business-analytics/summary',method:'get'})}
|
||||||
|
export function generateReport(d){return request({url:'/report/analytics/generate',method:'post',data:d})}
|
||||||
|
export function exportToExcel(p){return request({url:'/report/analytics/export',method:'get',params:p,responseType:'blob'})}
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
>
|
>
|
||||||
刷新
|
刷新
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
@click="generateReport"
|
||||||
|
>
|
||||||
|
生成报告
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="warning"
|
type="warning"
|
||||||
@click="exportReport"
|
@click="exportReport"
|
||||||
@@ -188,7 +194,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {ref, onMounted} from 'vue'
|
import {ref, onMounted} from 'vue'
|
||||||
import {ElMessage} from 'element-plus'
|
import {ElMessage} from 'element-plus'
|
||||||
import {getAnalyticsPage, getAnalyticsSummary} from './api'
|
import {getAnalyticsPage, getAnalyticsSummary, generateReport as apiGenerateReport, exportToExcel} from './api'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const analyticsData = ref([])
|
const analyticsData = ref([])
|
||||||
@@ -233,7 +239,48 @@ function resetQuery() {
|
|||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportReport() { ElMessage.info('导出功能开发中') }
|
function exportReport() {
|
||||||
|
const params = {}
|
||||||
|
if (q.value.departmentName) params.departmentName = q.value.departmentName
|
||||||
|
if (q.value.dateRange && q.value.dateRange.length === 2) {
|
||||||
|
params.startDate = q.value.dateRange[0]
|
||||||
|
params.endDate = q.value.dateRange[1]
|
||||||
|
}
|
||||||
|
exportToExcel(params).then(r => {
|
||||||
|
if (r.data && r.data.headers) {
|
||||||
|
const csvRows = [r.data.headers.join(',')]
|
||||||
|
r.data.data.forEach(row => csvRows.push(row.join(',')))
|
||||||
|
const blob = new Blob(['\ufeff' + csvRows.join('\n')], {type:'text/csv;charset=utf-8'})
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = URL.createObjectURL(blob)
|
||||||
|
link.download = '经营分析报告.csv'
|
||||||
|
link.click()
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning('无数据可导出')
|
||||||
|
}
|
||||||
|
}).catch(() => ElMessage.error('导出失败'))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateReport() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params = {}
|
||||||
|
if (q.value.departmentName) params.departmentName = q.value.departmentName
|
||||||
|
if (q.value.dateRange && q.value.dateRange.length === 2) {
|
||||||
|
params.startDate = q.value.dateRange[0]
|
||||||
|
params.endDate = q.value.dateRange[1]
|
||||||
|
}
|
||||||
|
const r = await apiGenerateReport(params)
|
||||||
|
if (r.data) {
|
||||||
|
statCards.value[0].value = formatMoney(r.data.totalRevenue)
|
||||||
|
statCards.value[1].value = formatMoney(r.data.totalCost)
|
||||||
|
statCards.value[2].value = formatMoney(r.data.totalProfit)
|
||||||
|
statCards.value[3].value = r.data.totalPatients || 0
|
||||||
|
}
|
||||||
|
ElMessage.success('报告生成完成')
|
||||||
|
} finally { loading.value = false }
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => loadData())
|
onMounted(() => loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user