Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
wangjian963
2026-06-02 16:03:09 +08:00
7 changed files with 797 additions and 560 deletions

View File

@@ -39,6 +39,16 @@ public class HomeStatisticsDto {
* 相对前日变化百分比
*/
private Double revenueTrend;
/**
* 今日收入金额(数值,单位:元)
*/
private java.math.BigDecimal todayRevenueAmount;
/**
* 昨日收入金额(数值,单位:元)
*/
private java.math.BigDecimal yesterdayRevenueAmount;
/**
* 今日预约数量
@@ -69,4 +79,19 @@ public class HomeStatisticsDto {
* 待写病历数量
*/
private Integer pendingEmr;
}
/**
* 今日处方数量
*/
private Integer todayPrescriptions;
/**
* 昨日处方数量
*/
private Integer yesterdayPrescriptions;
/**
* 处方相对前日变化百分比
*/
private Double prescriptionTrend;
}

View File

@@ -13,7 +13,16 @@ import com.openhis.administration.service.IPatientService;
import com.openhis.administration.service.IPractitionerService;
import com.openhis.common.enums.ParticipantType;
import com.openhis.web.dto.HomeStatisticsDto;
import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.financial.service.IPaymentReconciliationService;
import com.openhis.medication.domain.MedicationRequest;
import com.openhis.medication.service.IMedicationRequestService;
import com.openhis.common.enums.PaymentStatus;
import com.openhis.web.service.IHomeStatisticsService;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import com.openhis.web.patientmanage.mapper.PatientManageMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -46,6 +55,12 @@ public class HomeStatisticsServiceImpl implements IHomeStatisticsService {
@Autowired
private IPatientService patientService;
@Autowired
private IPaymentReconciliationService paymentReconciliationService;
@Autowired
private IMedicationRequestService medicationRequestService;
/**
* 获取首页统计数据
*
@@ -105,18 +120,108 @@ public class HomeStatisticsServiceImpl implements IHomeStatisticsService {
double patientTrend = calculateTrend(totalPatients, yesterdayPatients);
statistics.setPatientTrend(patientTrend);
// 今日收入和预约等其他统计暂时设为0后续从相应表查询
statistics.setTodayRevenue("¥ 0");
statistics.setYesterdayRevenue("¥ 0");
statistics.setRevenueTrend(0.0);
// 查询今日收入
BigDecimal todayRevenue = queryRevenueByDate(new Date());
BigDecimal yesterdayRevenue = queryRevenueByDate(getYesterday());
java.text.DecimalFormat df = new java.text.DecimalFormat("#,##0.00");
statistics.setTodayRevenue("¥ " + df.format(todayRevenue));
statistics.setYesterdayRevenue("¥ " + df.format(yesterdayRevenue));
statistics.setTodayRevenueAmount(todayRevenue);
statistics.setYesterdayRevenueAmount(yesterdayRevenue);
statistics.setRevenueTrend(calculateTrend(todayRevenue.doubleValue(), yesterdayRevenue.doubleValue()));
// 今日预约和待审核暂时设为0后续实现
statistics.setTodayAppointments(0);
statistics.setYesterdayAppointments(0);
statistics.setAppointmentTrend(0.0);
statistics.setPendingApprovals(0);
// 查询今日处方数量
int todayPrescriptions = queryPrescriptionCountByDate(new Date(), practitioner);
int yesterdayPrescriptions = queryPrescriptionCountByDate(getYesterday(), practitioner);
statistics.setTodayPrescriptions(todayPrescriptions);
statistics.setYesterdayPrescriptions(yesterdayPrescriptions);
statistics.setPrescriptionTrend(calculateTrend(todayPrescriptions, yesterdayPrescriptions));
return statistics;
}
/**
* 查询指定日期的处方数量
*
* @param date 日期
* @param practitioner 当前医生null 则查全部)
* @return 处方数量
*/
private int queryPrescriptionCountByDate(Date date, Practitioner practitioner) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date dayStart = cal.getTime();
cal.add(Calendar.DAY_OF_MONTH, 1);
Date dayEnd = cal.getTime();
LambdaQueryWrapper<MedicationRequest> query = new LambdaQueryWrapper<>();
query.ge(MedicationRequest::getCreateTime, dayStart)
.lt(MedicationRequest::getCreateTime, dayEnd)
.eq(MedicationRequest::getDeleteFlag, "0");
// 如果是医生角色,只统计自己开的处方
if (practitioner != null) {
query.eq(MedicationRequest::getPractitionerId, practitioner.getId());
}
return (int) medicationRequestService.count(query);
}
/**
* 查询指定日期的收款总额(状态为支付成功且未全部退款)
*
* @param date 日期
* @return 收款总额
*/
private BigDecimal queryRevenueByDate(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date dayStart = cal.getTime();
cal.add(Calendar.DAY_OF_MONTH, 1);
Date dayEnd = cal.getTime();
LambdaQueryWrapper<PaymentReconciliation> query = new LambdaQueryWrapper<>();
query.ge(PaymentReconciliation::getBillDate, dayStart)
.lt(PaymentReconciliation::getBillDate, dayEnd)
.eq(PaymentReconciliation::getStatusEnum, PaymentStatus.SUCCESS.getValue())
.eq(PaymentReconciliation::getDeleteFlag, "0")
.select(PaymentReconciliation::getDisplayAmount);
java.util.List<PaymentReconciliation> list = paymentReconciliationService.list(query);
if (list == null || list.isEmpty()) {
return BigDecimal.ZERO;
}
return list.stream()
.map(p -> p.getDisplayAmount() != null ? p.getDisplayAmount() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
/**
* 获取昨天的日期
*/
private Date getYesterday() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, -1);
return cal.getTime();
}
/**
* 计算相对前日的百分比变化
*

View File

@@ -0,0 +1,93 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: org.postgresql.Driver
druid:
# 主库数据源
master:
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=hisdev&characterEncoding=UTF-8&client_encoding=UTF-8
username: postgresql
password: Jchl1528 # 请替换为实际的数据库密码
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: true # 改为true以确保连接有效
testOnReturn: false
# 配置监控统计拦截的filters去掉后监控界面sql无法统计'wall'用于防火墙
filters: stat,wall,slf4j
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: openhis
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# redis 配置
redis:
# 地址
host: 192.168.110.252
# 端口默认为6379
port: 6379
# 数据库索引
database: 1
# 密码
password: Jchl1528
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 服务器配置
server:
# 服务器的HTTP端口默认为18080
port: 18080
servlet:
# 应用的访问路径
context-path: /openhis

View File

@@ -64,7 +64,9 @@
"vue-area-linkage": "^5.1.0",
"vue-cropper": "^1.1.1",
"vue-plugin-hiprint": "^0.0.19",
"vue-router": "^4.3.0"
"vue-router": "^4.3.0",
"vxe-table": "^4.19.6",
"xe-utils": "^3.9.1"
},
"devDependencies": {
"@playwright/test": "^1.58.2",

View File

@@ -1,5 +1,7 @@
import {createApp} from 'vue';
import VxeUIAll from 'vxe-table';
import 'vxe-table/lib/style.css';
import Cookies from 'js-cookie';
// 导入 hiprint 并挂载到全局 window 对象
@@ -122,6 +124,7 @@ directive(app);
// 全局禁止点击遮罩层关闭弹窗
ElDialog.props.closeOnClickModal.default = false;
// 使用element-plus 并且设置全局的大小
app.use(VxeUIAll);
app.use(ElementPlus, {
locale: zhCn,
// 支持 large、default、small

File diff suppressed because it is too large Load Diff

View File

@@ -75,23 +75,23 @@
</el-button>
</el-col>
</el-row>
<el-table
<vxe-table
v-if="refreshTable"
ref="tableRef"
v-loading="loading"
:data="definitionList1"
tooltip-effect="dark"
:show-overflow-tooltip="true"
show-overflow
:edit-config="{trigger: 'click', mode: 'cell' }"
style="width: 100% !important"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
<vxe-column
type="checkbox"
width="40"
align="center"
fixed="left"
/>
<el-table-column
<vxe-column
label="名称"
prop="name"
align="center"
@@ -117,8 +117,8 @@
</template>
</PopoverList>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="编码"
prop="busNo"
align="center"
@@ -126,9 +126,9 @@
<template #default="scope">
{{ scope.row.busNo ? scope.row.busNo : '-' }}
</template>
</el-table-column>
</vxe-column>
<el-table-column
<vxe-column
label="规格"
prop="volume"
align="center"
@@ -136,8 +136,8 @@
<template #default="scope">
{{ scope.row.volume ? scope.row.volume : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="当前进货价"
prop="originBuyingPrice"
align="center"
@@ -145,10 +145,11 @@
<template #default="scope">
{{ scope.row.originBuyingPrice ? scope.row.originBuyingPrice : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调后进货价"
prop="newBuyingPrice"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -158,8 +159,8 @@
step="0.01"
/>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="当前零售价"
prop="originRetailPrice"
align="center"
@@ -167,10 +168,11 @@
<template #default="scope">
{{ scope.row.originRetailPrice ? scope.row.originRetailPrice : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调后零售价"
prop="newRetailPrice"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -180,10 +182,11 @@
step="0.01"
/>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调价理由"
prop="reason"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -195,8 +198,8 @@
autosize
/>
</template>
</el-table-column>
</el-table>
</vxe-column>
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNo"
@@ -268,23 +271,23 @@
</el-button>
</el-col>
</el-row>
<el-table
<vxe-table
v-if="refreshTable"
ref="tableRef"
v-loading="loading"
:data="definitionList2"
tooltip-effect="dark"
:show-overflow-tooltip="true"
show-overflow
:edit-config="{trigger: 'click', mode: 'cell' }"
style="width: 100% !important"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
<vxe-column
type="checkbox"
width="40"
align="center"
fixed="left"
/>
<el-table-column
<vxe-column
label="名称"
prop="name"
align="center"
@@ -310,8 +313,8 @@
</template>
</PopoverList>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="编码"
prop="busNo"
align="center"
@@ -319,8 +322,8 @@
<template #default="scope">
{{ scope.row.busNo ? scope.row.busNo : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="规格"
prop="volume"
align="center"
@@ -328,8 +331,8 @@
<template #default="scope">
{{ scope.row.volume ? scope.row.volume : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="当前进货价"
prop="originBuyingPrice"
align="center"
@@ -337,10 +340,11 @@
<template #default="scope">
{{ scope.row.originBuyingPrice ? scope.row.originBuyingPrice : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调后进货价"
prop="newBuyingPrice"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -350,8 +354,8 @@
step="0.01"
/>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="当前零售价"
prop="originRetailPrice"
align="center"
@@ -359,10 +363,11 @@
<template #default="scope">
{{ scope.row.originRetailPrice ? scope.row.originRetailPrice : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调后零售价"
prop="newRetailPrice"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -372,10 +377,11 @@
step="0.01"
/>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调价理由"
prop="reason"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -387,8 +393,8 @@
autosize
/>
</template>
</el-table-column>
</el-table>
</vxe-column>
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNo"
@@ -460,23 +466,23 @@
</el-button>
</el-col>
</el-row>
<el-table
<vxe-table
v-if="refreshTable"
ref="tableRef"
v-loading="loading"
:data="definitionList3"
tooltip-effect="dark"
:show-overflow-tooltip="true"
show-overflow
:edit-config="{trigger: 'click', mode: 'cell' }"
style="width: 100% !important"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
<vxe-column
type="checkbox"
width="40"
align="center"
fixed="left"
/>
<el-table-column
<vxe-column
label="名称"
prop="name"
align="center"
@@ -502,8 +508,8 @@
</template>
</PopoverList>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="编码"
prop="busNo"
align="center"
@@ -511,9 +517,9 @@
<template #default="scope">
{{ scope.row.busNo ? scope.row.busNo : '-' }}
</template>
</el-table-column>
</vxe-column>
<el-table-column
<vxe-column
label="当前零售价"
prop="originRetailPrice"
align="center"
@@ -521,10 +527,11 @@
<template #default="scope">
{{ scope.row.originRetailPrice ? scope.row.originRetailPrice : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调后零售价"
prop="newRetailPrice"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -534,10 +541,11 @@
step="0.01"
/>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调价理由"
prop="reason"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -549,8 +557,8 @@
autosize
/>
</template>
</el-table-column>
</el-table>
</vxe-column>
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNo"
@@ -652,22 +660,22 @@
</el-col>
</el-row>
<el-table
<vxe-table
v-if="refreshTable"
ref="tableRef"
v-loading="loading"
:data="definitionList4"
tooltip-effect="dark"
:show-overflow-tooltip="true"
show-overflow
:edit-config="{trigger: 'click', mode: 'cell' }"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
<vxe-column
type="checkbox"
width="40"
align="center"
fixed="left"
/>
<el-table-column
<vxe-column
label="名称"
prop="orgName"
align="center"
@@ -677,8 +685,8 @@
{{ scope.row.orgName ? scope.row.orgName : '-' }}
</span>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="类型"
min-width="120"
align="center"
@@ -701,8 +709,8 @@
</el-select>
<span v-else>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="当前零售价"
prop="originRetailPrice"
align="center"
@@ -710,10 +718,11 @@
<template #default="scope">
{{ scope.row.originRetailPrice ? scope.row.originRetailPrice : '-' }}
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调后零售价"
prop="newRetailPrice"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -723,10 +732,11 @@
step="0.01"
/>
</template>
</el-table-column>
<el-table-column
</vxe-column>
<vxe-column
label="调价理由"
prop="reason"
:edit-render="{}"
align="center"
>
<template #default="scope">
@@ -738,8 +748,8 @@
autosize
/>
</template>
</el-table-column>
</el-table>
</vxe-column>
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNo"