Merge branch 'develop' of https://gitea.gentronhealth.com/py/his into develop

This commit is contained in:
2026-02-03 14:00:59 +08:00
22 changed files with 1746 additions and 28 deletions

View File

@@ -10,16 +10,21 @@ import com.core.common.utils.MessageUtils;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils;
import com.core.common.utils.bean.BeanUtils;
import com.core.common.core.domain.entity.SysRole;
import com.core.common.core.domain.model.LoginUser;
import com.openhis.administration.domain.*;
import com.openhis.administration.mapper.PatientMapper;
import com.openhis.administration.service.*;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.common.enums.*;
import com.openhis.common.enums.ybenums.YbPayment;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisPageUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.financial.domain.RefundLog;
import com.openhis.financial.service.IRefundLogService;
import com.openhis.web.basicservice.dto.HealthcareServiceDto;
import com.openhis.web.basicservice.mapper.HealthcareServiceBizMapper;
import com.openhis.web.chargemanage.appservice.IOutpatientRegistrationAppService;
@@ -40,10 +45,14 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 门诊挂号 应用实现类
@@ -85,6 +94,9 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
@Resource
TriageCandidateExclusionService triageCandidateExclusionService;
@Resource
IRefundLogService iRefundLogService;
/**
* 门诊挂号 - 查询患者信息
*
@@ -260,7 +272,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
R<?> result = iPaymentRecService.cancelRegPayment(cancelPaymentDto);
PaymentReconciliation paymentRecon = null;
if (PaymentReconciliation.class.isAssignableFrom(result.getData().getClass())) {
if (result.getData() != null && PaymentReconciliation.class.isAssignableFrom(result.getData().getClass())) {
paymentRecon = (PaymentReconciliation)result.getData();
}
if (paymentRecon != null) {
@@ -275,6 +287,9 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
}
}
// 记录退号日志
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon);
// 2025/05/05 该处保存费用项后,会通过统一收费处理进行收费
return R.ok(paymentRecon, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"退号"}));
}
@@ -394,4 +409,124 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
return R.ok(null, "补打挂号成功");
}
/**
* 记录退号日志
*
* @param cancelRegPaymentDto 退号请求对象
* @param encounter 就诊信息
* @param result 退号结果
* @param paymentRecon 支付对账信息
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordRefundLog(CancelRegPaymentDto cancelRegPaymentDto,
Encounter encounter,
R<?> result,
PaymentReconciliation paymentRecon) {
RefundLog refundLog = new RefundLog();
try {
// 1. 订单ID唯一
String orderId = String.valueOf(cancelRegPaymentDto.getEncounterId());
refundLog.setOrderId(orderId);
// 已存在则不重复插入(防止唯一约束异常)
long exist = iRefundLogService.lambdaQuery()
.eq(RefundLog::getOrderId, orderId)
.count();
if (exist > 0) {
log.warn("退号日志已存在orderId={}", orderId);
return;
}
// 2. 患者信息
if (encounter != null) {
refundLog.setPatientId(String.valueOf(encounter.getPatientId()));
Patient patient = patientMapper.selectById(encounter.getPatientId());
refundLog.setPatientName(patient != null ? patient.getName() : "未知");
} else {
refundLog.setPatientId("0");
refundLog.setPatientName("未知");
}
// 3. 金额
// 优先使用paymentRecon中的displayAmount如果没有则尝试使用cancelRegPaymentDto中的displayAmount
BigDecimal refundAmount = BigDecimal.ZERO;
if (paymentRecon != null) {
// 使用退号后生成的paymentRecon的实际金额这个金额应该是负数
refundAmount = paymentRecon.getDisplayAmount() != null
? paymentRecon.getDisplayAmount().abs() // 取绝对值,因为退费金额通常显示为正数
: BigDecimal.ZERO;
} else if (cancelRegPaymentDto.getDisplayAmount() != null) {
refundAmount = cancelRegPaymentDto.getDisplayAmount();
}
refundLog.setRefundAmount(refundAmount);
// 4. 原因 & 类型
refundLog.setRefundReason(
StringUtils.isNotEmpty(cancelRegPaymentDto.getReason())
? cancelRegPaymentDto.getReason()
: "诊前退号-已缴费签到未就诊"
);
refundLog.setRefundType("FULL");
// 5. 退款方式
String refundMethod = "UNKNOWN";
if (cancelRegPaymentDto.getPaymentDetails() != null
&& !cancelRegPaymentDto.getPaymentDetails().isEmpty()) {
Integer payEnum = cancelRegPaymentDto.getPaymentDetails().get(0).getPayEnum();
if (payEnum != null) {
YbPayment ybPayment = YbPayment.getByValue(payEnum);
refundMethod = (ybPayment != null ? ybPayment.getInfo() : payEnum.toString());
}
}
refundLog.setRefundMethod(refundMethod);
// 6. 原交易号
refundLog.setOriginalTradeNo(
paymentRecon != null ? paymentRecon.getPaymentNo() : null
);
// 7. 时间
refundLog.setRefundTime(LocalDateTime.now());
// 8. 操作人
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null) {
refundLog.setOpUserId(String.valueOf(loginUser.getUserId()));
refundLog.setOpUserName(loginUser.getUsername());
List<SysRole> roles = loginUser.getRoleList();
refundLog.setOpRole(
roles != null && !roles.isEmpty() ? roles.get(0).getRoleName() : "SYSTEM"
);
} else {
refundLog.setOpUserId("0");
refundLog.setOpUserName("SYSTEM");
refundLog.setOpRole("SYSTEM");
}
// 9. 状态
if (result != null && result.getCode() == 200) {
refundLog.setState(1);
} else {
refundLog.setState(0);
refundLog.setFailReason(result != null ? result.getMsg() : "未知错误");
}
// 10. 创建时间
refundLog.setCreatedAt(LocalDateTime.now());
refundLog.setUpdatedAt(LocalDateTime.now());
// 11. 保存
boolean saved = iRefundLogService.save(refundLog);
if (!saved) {
throw new RuntimeException("退号日志保存失败");
}
log.info("退号日志入库成功, orderId={}", orderId);
} catch (Exception e) {
log.error("退号日志入库失败,数据={}", refundLog, e);
throw e; // 让事务感知(你也可以只记录不抛)
}
}
}

View File

@@ -59,4 +59,14 @@ public class HomeStatisticsDto {
* 待审核数量
*/
private Integer pendingApprovals;
/**
* 我的患者数量(医生专属)
*/
private Integer myPatients;
/**
* 待写病历数量
*/
private Integer pendingEmr;
}

View File

@@ -136,6 +136,7 @@ public class PatientInformationServiceImpl implements IPatientInformationService
// 获取登录者信息
LoginUser loginUser = SecurityUtils.getLoginUser();
Long userId = loginUser.getUserId();
Integer tenantId = loginUser.getTenantId().intValue();
// 先构建基础查询条件
QueryWrapper<PatientBaseInfoDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
@@ -159,8 +160,9 @@ public class PatientInformationServiceImpl implements IPatientInformationService
if (practitioner != null) {
// 查询该医生作为接诊医生ADMITTER, code="1"和挂号医生REGISTRATION_DOCTOR, code="12"的所有就诊记录的患者ID
List<Long> doctorPatientIds = patientManageMapper.getPatientIdsByPractitionerId(
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()));
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()),
tenantId);
if (doctorPatientIds != null && !doctorPatientIds.isEmpty()) {
// 添加患者ID过滤条件 - 注意:这里使用列名而不是表别名

View File

@@ -64,8 +64,10 @@ public interface PatientManageMapper extends BaseMapper<Patient> {
*
* @param practitionerId 医生ID
* @param typeCodes 参与者类型代码列表
* @param tenantId 租户ID
* @return 患者ID列表
*/
List<Long> getPatientIdsByPractitionerId(@Param("practitionerId") Long practitionerId,
@Param("typeCodes") List<String> typeCodes);
List<Long> getPatientIdsByPractitionerId(@Param("practitionerId") Long practitionerId,
@Param("typeCodes") List<String> typeCodes,
@Param("tenantId") Integer tenantId);
}

View File

@@ -66,23 +66,34 @@ public class HomeStatisticsServiceImpl implements IHomeStatisticsService {
Practitioner practitioner = practitionerList != null && !practitionerList.isEmpty() ? practitionerList.get(0) : null;
int totalPatients = 0;
int myPatients = 0;
// 如果当前用户是医生,查询该医生接诊和被挂号的所有患者
if (practitioner != null) {
// 获取当前登录用户的租户ID
Integer tenantId = SecurityUtils.getLoginUser().getTenantId().intValue();
// 查询该医生作为接诊医生ADMITTER, code="1"和挂号医生REGISTRATION_DOCTOR, code="12"的所有就诊记录的患者ID
List<Long> doctorPatientIds = patientManageMapper.getPatientIdsByPractitionerId(
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()));
totalPatients = doctorPatientIds != null ? doctorPatientIds.size() : 0;
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()),
tenantId);
myPatients = doctorPatientIds != null ? doctorPatientIds.size() : 0;
// 对于医生,"我的患者"数量即为该医生负责的患者数量
statistics.setMyPatients(myPatients);
} else {
// 如果不是医生,查询所有患者(与患者管理页面逻辑保持一致)
LambdaQueryWrapper<Patient> patientQuery = new LambdaQueryWrapper<>();
patientQuery.eq(Patient::getDeleteFlag, "0");
List<Patient> patientList = patientService.list(patientQuery);
totalPatients = patientList != null ? patientList.size() : 0;
// 如果不是医生,"我的患者"数量为0
statistics.setMyPatients(0);
}
// 查询所有患者作为总患者数
LambdaQueryWrapper<Patient> patientQuery = new LambdaQueryWrapper<>();
patientQuery.eq(Patient::getDeleteFlag, "0");
List<Patient> patientList = patientService.list(patientQuery);
totalPatients = patientList != null ? patientList.size() : 0;
statistics.setTotalPatients(totalPatients);
// 查询昨日在院患者数量(暂时简化处理)

View File

@@ -3,6 +3,8 @@ package com.openhis.web.system.controller;
import com.core.common.core.domain.R;
import com.core.common.utils.SecurityUtils;
import com.openhis.web.doctorstation.appservice.IDoctorStationEmrAppService;
import com.openhis.web.dto.HomeStatisticsDto;
import com.openhis.web.service.IHomeStatisticsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
@@ -20,19 +22,21 @@ import org.springframework.web.bind.annotation.RestController;
public class HomeController {
private final IDoctorStationEmrAppService doctorStationEmrAppService;
private final IHomeStatisticsService homeStatisticsService;
@ApiOperation("获取首页统计数据")
@GetMapping("/statistics")
public R<?> getStatistics() {
// 这里可以返回各种统计数据
// 为了简化,我们只返回待写病历数量
// 获取基础统计数据
HomeStatisticsDto statisticsDto = homeStatisticsService.getHomeStatistics();
// 获取待写病历数量
Long userId = SecurityUtils.getLoginUser().getUserId();
R<?> pendingEmrCount = doctorStationEmrAppService.getPendingEmrCount(userId);
// 构建返回数据
java.util.Map<String, Object> data = new java.util.HashMap<>();
data.put("pendingEmr", pendingEmrCount.getData());
return R.ok(data);
// 将待写病历数量添加到统计数据
statisticsDto.setPendingEmr((Integer) pendingEmrCount.getData());
return R.ok(statisticsDto);
}
}

View File

@@ -136,13 +136,13 @@
<select id="getPatientIdsByPractitionerId" resultType="java.lang.Long">
SELECT DISTINCT enc.patient_id
FROM adm_encounter_participant AS ep
LEFT JOIN adm_encounter AS enc ON ep.encounter_id = enc.ID AND enc.delete_flag = '0'
INNER JOIN adm_encounter AS enc ON ep.encounter_id = enc.ID AND enc.delete_flag = '0'
INNER JOIN adm_patient AS pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
WHERE ep.delete_flag = '0'
AND ep.practitioner_id = #{practitionerId}
AND ep.tenant_id = 1
AND enc.tenant_id = 1
AND pt.tenant_id = 1
AND ep.tenant_id = #{tenantId}
AND enc.tenant_id = #{tenantId}
AND pt.tenant_id = #{tenantId}
<if test="typeCodes != null and !typeCodes.isEmpty()">
AND ep.type_code IN
<foreach collection="typeCodes" item="typeCode" open="(" separator="," close=")">