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

This commit is contained in:
Ranyunqiao
2026-06-11 10:05:41 +08:00
14 changed files with 88 additions and 53 deletions

View File

@@ -153,7 +153,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
dto.setIdCard(raw.getIdCard());
dto.setDoctorId(raw.getDoctorId());
dto.setDepartmentId(raw.getDepartmentId());
dto.setRealPatientId(raw.getPatientId());
dto.setRealPatientId(raw.getRealPatientId() != null ? raw.getRealPatientId() : raw.getPatientId());
dto.setOrderId(raw.getOrderId());
dto.setOrderNo(raw.getOrderNo());

View File

@@ -515,29 +515,28 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
// 构建查询条件
QueryWrapper<CurrentDayEncounterDto> queryWrapper = HisQueryUtils.buildQueryWrapper(null, searchKey,
new HashSet<>(Arrays.asList("patient_name", "organization_name", "practitioner_name", "healthcare_name", "identifier_no")),
request);
null); // registerTimeSTime/ETime 已下推到 SQL 内层 WHERE跳过 buildQueryWrapper 的自动 *STime/*ETime 处理
// 手动处理 statusEnum 参数(用于过滤退号记录
// 提取statusEnum参数下推到内层WHERE避免外层重复过滤
Integer statusFilter = null;
String statusEnumParam = request.getParameter("statusEnum");
if (statusEnumParam != null && !statusEnumParam.isEmpty()) {
try {
Integer statusEnum = Integer.parseInt(statusEnumParam);
if (statusEnum == -1) {
// -1 表示排除退号记录(正常挂号)
queryWrapper.ne("status_enum", 6);
} else {
// 其他值表示精确匹配
queryWrapper.eq("status_enum", statusEnum);
}
statusFilter = Integer.parseInt(statusEnumParam);
} catch (NumberFormatException e) {
// 忽略无效的参数值
}
}
// 提取日期范围参数下推到内层WHERE以优化性能避免全表JOIN后再过滤
String registerTimeSTime = request.getParameter("registerTimeSTime");
String registerTimeETime = request.getParameter("registerTimeETime");
IPage<CurrentDayEncounterDto> currentDayEncounter = outpatientRegistrationAppMapper.getCurrentDayEncounter(
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue());
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue(),
registerTimeSTime, registerTimeETime, statusFilter);
// 过滤候选池排除列表
// 仅当调用方显式传 excludeFromCandidatePool=true 时才过滤,避免非分诊场景(挂号/收费)

View File

@@ -54,7 +54,10 @@ public interface OutpatientRegistrationAppMapper {
@Param("classEnum") Integer classEnum, @Param("statusEnum") Integer statusEnum,
@Param("participantType1") String participantType1, @Param("participantType2") String participantType2,
@Param(Constants.WRAPPER) QueryWrapper<CurrentDayEncounterDto> queryWrapper,
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus);
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus,
@Param("registerTimeSTime") String registerTimeSTime,
@Param("registerTimeETime") String registerTimeETime,
@Param("statusFilter") Integer statusFilter);
/**
* 查询item绑定的信息(耗材或诊疗)

View File

@@ -451,10 +451,12 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
// 删除费用项
iChargeItemService.deleteByServiceTableAndId(CommonConstants.TableName.MED_MEDICATION_REQUEST,
adviceSaveDto.getRequestId());
// 删除代煎费
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
.eq(ChargeItem::getPrescriptionNo, adviceSaveDto.getPrescriptionNo())
.eq(ChargeItem::getProductId, sufferingDefinitionId));
// 删除代煎费(按处方号精确清理)
if (adviceSaveDto.getPrescriptionNo() != null) {
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
.eq(ChargeItem::getPrescriptionNo, adviceSaveDto.getPrescriptionNo())
.eq(ChargeItem::getProductId, sufferingDefinitionId));
}
}
@@ -614,7 +616,7 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
Long encounterDiagnosisId = medicineList.get(0).getEncounterDiagnosisId();
// 中药付数
BigDecimal chineseHerbsDoseQuantity = medicineList.get(0).getChineseHerbsDoseQuantity();
// 🔧 Bug Fix #668: 收集所有处方号(不同分组可能有不同处方号)
// 收集所有处方号(不同分组可能有不同处方号)
List<String> prescriptionNos = insertOrUpdateList.stream()
.map(AdviceSaveDto::getPrescriptionNo)
.filter(Objects::nonNull)
@@ -628,12 +630,10 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
AdviceBaseDto adviceBaseDto = new AdviceBaseDto();
adviceBaseDto.setAdviceDefinitionId(sufferingDefinitionId); // 医嘱定义id
// 🔧 Bug Fix #668: 先删除所有处方号关联的中药代煎账单
if (!prescriptionNos.isEmpty()) {
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
.in(ChargeItem::getPrescriptionNo, prescriptionNos)
.eq(ChargeItem::getProductId, sufferingDefinitionId));
}
// 先删除该就诊关联的所有中药代煎账单
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
.eq(ChargeItem::getEncounterId, encounterId)
.eq(ChargeItem::getProductId, sufferingDefinitionId));
// 对应的诊疗医嘱信息
AdviceBaseDto activityAdviceBaseDto = iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null,
@@ -642,7 +642,7 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
// 费用定价
AdvicePriceDto advicePriceDto = activityAdviceBaseDto.getPriceList().get(0);
if (advicePriceDto != null) {
// 🔧 Bug Fix #668: 为每个处方号分别生成代煎账单
// 为每个处方号分别生成代煎账单
for (String prescriptionNo : prescriptionNos) {
chargeItem = new ChargeItem();
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
@@ -674,12 +674,10 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
}
}
} else if (Whether.NO.getValue().equals(sufferingFlag)) {
// 🔧 Bug Fix #668: 删除所有处方号关联的中药代煎账单
if (!prescriptionNos.isEmpty()) {
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
.in(ChargeItem::getPrescriptionNo, prescriptionNos)
.eq(ChargeItem::getProductId, sufferingDefinitionId));
}
// 删除该就诊关联的所有中药代煎账单
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
.eq(ChargeItem::getEncounterId, encounterId)
.eq(ChargeItem::getProductId, sufferingDefinitionId));
}
// 签发时,把草稿状态的账单更新为待收费[中医]

View File

@@ -192,6 +192,9 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
// 提取requestStatus手动处理支持COMPLETED(3)和CHECK_VERIFIED(10)同时查询
Integer requestStatus = inpatientAdviceParam.getRequestStatus();
inpatientAdviceParam.setRequestStatus(null);
// Bug #714: 提取deadline手动处理UNION子查询列名为end_time
String deadline = inpatientAdviceParam.getDeadline();
inpatientAdviceParam.setDeadline(null);
// 构建查询条件
QueryWrapper<InpatientAdviceParam> queryWrapper
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
@@ -223,6 +226,16 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
if (therapyEnum != null) {
queryWrapper.and(w -> w.eq("therapy_enum", therapyEnum).or().isNull("therapy_enum"));
}
// Bug #714: 手动拼接deadline条件按医嘱截止时间筛选
if (deadline != null && !deadline.isEmpty()) {
try {
LocalDateTime deadlineTime = LocalDateTime.parse(deadline,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
queryWrapper.le("end_time", deadlineTime);
} catch (DateTimeParseException e) {
// 忽略无效的日期格式
}
}
// 患者医嘱分页列表
Page<InpatientAdviceDto> inpatientAdvicePage
= adviceProcessAppMapper.selectInpatientAdvicePage(new Page<>(pageNo, pageSize), queryWrapper,

View File

@@ -0,0 +1,9 @@
-- Bug #735: 新医嘱签发后"停嘱医生"字段错误生成数据
-- 原因stopper_name 映射到 update_by 字段,签发时 MyBatis-Plus 自动填充导致错误赋值
-- 修复:添加专用 stopper_id 字段,仅在停嘱操作时设置
-- 药品请求表添加停嘱医生ID字段
ALTER TABLE med_medication_request ADD COLUMN IF NOT EXISTS stopper_id BIGINT;
-- 服务请求表添加停嘱医生ID字段
ALTER TABLE wor_service_request ADD COLUMN IF NOT EXISTS stopper_id BIGINT;

View File

@@ -217,6 +217,11 @@
WHERE T1.delete_flag = '0'
AND T1.class_enum = #{classEnum}
AND T10.context_enum = #{register}
AND (#{registerTimeSTime} IS NULL OR T1.create_time &gt;= CAST(#{registerTimeSTime} AS TIMESTAMP))
AND (#{registerTimeETime} IS NULL OR T1.create_time &lt;= CAST(#{registerTimeETime} AS TIMESTAMP))
AND (#{statusFilter} IS NULL
OR (#{statusFilter} &gt;= 0 AND T1.status_enum = #{statusFilter})
OR (#{statusFilter} = -1 AND T1.status_enum != 6))
) AS T9
${ew.customSqlSegment}
ORDER BY T9.register_time DESC

View File

@@ -108,8 +108,6 @@
<if test="statusEnum == 18">
T4.status_enum = #{submitted}
</if>
AND T4.summary_no IS NOT NULL
AND T4.summary_no != ''
) AS ii
${ew.customSqlSegment}
GROUP BY ii.encounter_id,
@@ -268,8 +266,6 @@
AND T15.delete_flag = '0'
WHERE T1.delete_flag = '0'
-- 因发药配药合并,前台只能看到待发药,已发药状态,但是后台配药发药状态都查
AND T1.summary_no IS NOT NULL
AND T1.summary_no != ''
AND
<if test="dispenseStatus == null">
T1.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared},#{summarized})

View File

@@ -222,8 +222,8 @@
T1.based_on_id AS based_on_id,
T1.medication_id AS advice_definition_id,
T1.content_json::jsonb ->> 'remark' AS remark,
T1.effective_dose_end AS stop_time,
T1.update_by AS stop_user_name
CASE WHEN T1.status_enum = 6 THEN T1.effective_dose_end ELSE NULL END AS stop_time,
CASE WHEN T1.status_enum = 6 THEN T1.update_by ELSE NULL END AS stop_user_name
FROM med_medication_request AS T1
LEFT JOIN adm_practitioner AS ap ON ap.id = T1.practitioner_id AND ap.delete_flag = '0'
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
@@ -339,8 +339,8 @@
T1.based_on_id AS based_on_id,
T1.activity_id AS advice_definition_id,
T1.remark AS remark,
T1.occurrence_end_time AS stop_time,
T1.update_by AS stop_user_name
CASE WHEN T1.status_enum = 6 THEN T1.occurrence_end_time ELSE NULL END AS stop_time,
CASE WHEN T1.status_enum = 6 THEN T1.update_by ELSE NULL END AS stop_user_name
FROM wor_service_request AS T1
LEFT JOIN adm_practitioner AS ap ON ap.id = T1.requester_id AND ap.delete_flag = '0'
LEFT JOIN wor_activity_definition AS T2

View File

@@ -21,6 +21,7 @@ public class TicketSlotDTO {
private String patientName;
private String medicalCard;
private Long patientId;
private Long realPatientId;
private String phone;
private Integer orderStatus;
private Long orderId;

View File

@@ -111,6 +111,7 @@
o.order_no AS orderNo,
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
pinfo.gender_enum AS genderEnum,
pinfo.id AS realPatientId,
pinfo.id_card AS idCard,
o.appointment_time AS appointmentTime,
<include refid="orderStatusNormExpr" /> AS orderStatus,
@@ -230,6 +231,7 @@
o.order_no AS orderNo,
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
pinfo.gender_enum AS genderEnum,
pinfo.id AS realPatientId,
pinfo.id_card AS idCard,
o.appointment_time AS appointmentTime,
<include refid="orderStatusNormExpr" /> AS orderStatus,

View File

@@ -61,7 +61,7 @@
ref="patientListRef"
height="620"
:data="patientList"
:row-config="{ keyField: 'encounterId', keyField: 'id' }"
:row-config="{ keyField: 'encounterId' }"
@cell-click="clickRow"
>
<vxe-column
@@ -447,7 +447,8 @@ function checkSelectable(row, index) {
/**
* 点击患者列表行 获取处方列表
*/
function clickRow(row) {
function clickRow(params) {
const row = params.row || params;
patientInfo.value = row;
chargeLoading.value = true;
encounterId.value = row.encounterId;

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div
v-loading="readCardLoading"
class="app-container"
@@ -122,6 +122,7 @@
<patientList
:searchkey="patientSearchKey"
@selsect-patient="selsectPatient"
@mousedown.prevent
/>
<template #reference>
<el-input
@@ -2079,10 +2080,20 @@ async function confirmCheckIn() {
// 每次开始新的签到流程先清理残留 slotId避免历史脏值串单
currentSlotId.value = null;
// 防御性校验:确保关键字段存在
if (!patient.departmentId) {
ElMessage.error('该号源缺少科室信息,无法完成签到,请联系管理员');
return;
}
if (!patient.realPatientId) {
ElMessage.error('该号源缺少患者信息,无法完成签到,请联系管理员');
return;
}
// 弹出确认提示
try {
await ElMessageBox.confirm(
`确认为患者【${patient.patientName}】办理签到挂号?\n` +
`确认为患者【${patient.patientName || '未知患者'}】办理签到挂号?\n` +
`科室:${patient.department || '-'}\n` +
`医生:${patient.doctor || '-'}\n` +
`费用:¥${patient.fee || '0.00'}`,
@@ -2215,7 +2226,7 @@ async function confirmCheckIn() {
* 点击患者列表给表单赋值
*/
function selsectPatient(row) {
form.value = { ...form.value, ...row };
Object.assign(form.value, row);
form.value.patientId = row.id;
form.value.searchKey = row.name;
form.value.name = row.name;

View File

@@ -16,7 +16,7 @@
v-model="type"
@change="handleRadioChange"
>
<el-radio :value="THERAPY_TYPE_ALL">
<el-radio :value="0">
全部
</el-radio>
<el-radio :value="1">
@@ -26,15 +26,13 @@
临时
</el-radio>
</el-radio-group>
<span style="flex-shrink: 0;">截止时间</span>
<el-date-picker
v-model="deadline"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
placeholder="选择截止时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:clearable="false"
style="width: 200px;"
style="width: 200px"
/>
<el-button
type="primary"
@@ -346,8 +344,7 @@ import {RequestStatus} from '@/utils/medicalConstants';
const activeNames = ref([]);
const prescriptionList = ref([]);
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
const THERAPY_TYPE_ALL = 0;
const type = ref(THERAPY_TYPE_ALL);
const type = ref(0);
const backReasonVisible = ref(false);
const backReasonForm = ref({ reason: '' });
const backReasonFormRef = ref(null);
@@ -447,8 +444,8 @@ function handleGetPrescription() {
getPrescriptionList({
encounterIds: encounterIds,
requestStatus: props.requestStatus,
...(type.value !== THERAPY_TYPE_ALL ? { therapyEnum: type.value } : {}),
deadline: deadline.value,
...(type.value !== 0 ? { therapyEnum: type.value } : {}),
...(deadline.value ? { deadline: deadline.value } : {}),
pageSize: 10000,
pageNo: 1,
}).then((res) => {