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 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 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
|
||||
type="success"
|
||||
@click="generateReport"
|
||||
>
|
||||
生成报告
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click="exportReport"
|
||||
@@ -188,7 +194,7 @@
|
||||
<script setup>
|
||||
import {ref, onMounted} from 'vue'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import {getAnalyticsPage, getAnalyticsSummary} from './api'
|
||||
import {getAnalyticsPage, getAnalyticsSummary, generateReport as apiGenerateReport, exportToExcel} from './api'
|
||||
|
||||
const loading = ref(false)
|
||||
const analyticsData = ref([])
|
||||
@@ -233,7 +239,48 @@ function resetQuery() {
|
||||
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())
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user