diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java index 502575caa..a5ba90a96 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -1189,6 +1189,28 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp if (medicationRequest.getId() == null) { firstTimeSave = true; } + // 🔧 Bug #643: 检测是否为状态回退操作(已签发 → 待签发) + // 当 is_save=true 且已有记录的 statusEnum 为 ACTIVE(2) 时, + // 说明前端请求将已签发医嘱回退到待签发状态,需要清除签发信息 + boolean isRollback = false; + if (is_save && !firstTimeSave) { + MedicationRequest existing = iMedicationRequestService.getById(medicationRequest.getId()); + if (existing != null) { + // 所有权校验:确保操作的医嘱属于当前就诊 + if (!existing.getEncounterId().equals(adviceSaveDto.getEncounterId())) { + log.error("Bug#643: 越权操作,医嘱 encounterId={} 与请求 encounterId={} 不匹配", + existing.getEncounterId(), adviceSaveDto.getEncounterId()); + throw new ServiceException("无权操作此医嘱"); + } + if (RequestStatus.ACTIVE.getValue().equals(existing.getStatusEnum())) { + isRollback = true; + // 保持原始 generateSourceEnum,避免被默认值覆盖导致刷新查询不到 + medicationRequest.setGenerateSourceEnum(existing.getGenerateSourceEnum()); + log.info("Bug#643: 检测到状态回退操作, requestId={}, statusEnum {} → {}", + medicationRequest.getId(), existing.getStatusEnum(), RequestStatus.DRAFT.getValue()); + } + } + } // 确保 contentJson 包含 remark if (adviceSaveDto.getRemark() != null && !adviceSaveDto.getRemark().isEmpty()) { medicationRequest.setContentJson(injectRemarkIntoContentJson(medicationRequest.getContentJson(), adviceSaveDto.getRemark())); @@ -1197,7 +1219,23 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp if (firstTimeSave) { medRequestIdList.add(medicationRequest.getId().toString()); } - if (is_save) { + // 🔧 Bug #643: 回退操作跳过发放/计费/绑耗逻辑,只清除签发信息 + if (isRollback) { + // 清除 medication_request.sign_code + UpdateWrapper signClearWrapper = new UpdateWrapper<>(); + signClearWrapper.eq("id", medicationRequest.getId()); + signClearWrapper.set("sign_code", null); + iMedicationRequestService.update(null, signClearWrapper); + log.info("Bug#643: 已清除 signCode, requestId={}", medicationRequest.getId()); + + // 回退 adm_charge_item.status_enum 从 BILLABLE(2) 到 DRAFT(0) + UpdateWrapper chargeRollbackWrapper = new UpdateWrapper<>(); + chargeRollbackWrapper.eq("service_id", medicationRequest.getId()); + chargeRollbackWrapper.eq("service_table", CommonConstants.TableName.MED_MEDICATION_REQUEST); + chargeRollbackWrapper.set("status_enum", ChargeItemStatus.DRAFT.getValue()); + iChargeItemService.update(null, chargeRollbackWrapper); + log.info("Bug#643: 已回退费用项状态为 DRAFT, requestId={}", medicationRequest.getId()); + } else if (is_save) { // 处理药品发放 Long dispenseId = iMedicationDispenseService.handleMedicationDispense(medicationRequest, adviceSaveDto.getDbOpType()); @@ -1658,8 +1696,40 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp if (adviceSaveDto.getRemark() != null && !adviceSaveDto.getRemark().isEmpty()) { deviceRequest.setContentJson(injectRemarkIntoContentJson(deviceRequest.getContentJson(), adviceSaveDto.getRemark())); } + // 🔧 Bug #643: 检测是否为耗材状态回退操作(已签发 → 待签发) + boolean deviceFirstTimeSave = (deviceRequest.getId() == null); + boolean deviceIsRollback = false; + if (is_save && !deviceFirstTimeSave) { + DeviceRequest existingDevice = iDeviceRequestService.getById(deviceRequest.getId()); + if (existingDevice != null) { + // 所有权校验 + if (!existingDevice.getEncounterId().equals(adviceSaveDto.getEncounterId())) { + log.error("Bug#643: 越权操作(耗材),医嘱 encounterId={} 与请求 encounterId={} 不匹配", + existingDevice.getEncounterId(), adviceSaveDto.getEncounterId()); + throw new ServiceException("无权操作此医嘱"); + } + if (RequestStatus.ACTIVE.getValue().equals(existingDevice.getStatusEnum())) { + deviceIsRollback = true; + // 保持原始 generateSourceEnum,避免被默认值覆盖导致刷新查询不到 + deviceRequest.setGenerateSourceEnum(existingDevice.getGenerateSourceEnum()); + log.info("Bug#643: 检测到耗材状态回退操作, requestId={}, statusEnum {} → {}", + deviceRequest.getId(), existingDevice.getStatusEnum(), RequestStatus.DRAFT.getValue()); + } + } + } + iDeviceRequestService.saveOrUpdate(deviceRequest); - if (is_save) { + // 🔧 Bug #643: 耗材回退操作跳过发放/计费逻辑,只清除签发信息 + if (deviceIsRollback) { + // 清除 wor_device_request.sign_code(如果该表有此字段) + // 回退 adm_charge_item.status_enum 从 BILLABLE(2) 到 DRAFT(0) + UpdateWrapper deviceChargeRollbackWrapper = new UpdateWrapper<>(); + deviceChargeRollbackWrapper.eq("service_id", deviceRequest.getId()); + deviceChargeRollbackWrapper.eq("service_table", CommonConstants.TableName.WOR_DEVICE_REQUEST); + deviceChargeRollbackWrapper.set("status_enum", ChargeItemStatus.DRAFT.getValue()); + iChargeItemService.update(null, deviceChargeRollbackWrapper); + log.info("Bug#643: 已回退耗材费用项状态为 DRAFT, requestId={}", deviceRequest.getId()); + } else if (is_save) { // 处理耗材发放 Long dispenseId = iDeviceDispenseService.handleDeviceDispense(deviceRequest, adviceSaveDto.getDbOpType()); diff --git a/healthlink-his-ui/src/views/surgicalschedule/index.vue b/healthlink-his-ui/src/views/surgicalschedule/index.vue index 3fc63d706..a7c899b2d 100755 --- a/healthlink-his-ui/src/views/surgicalschedule/index.vue +++ b/healthlink-his-ui/src/views/surgicalschedule/index.vue @@ -1632,7 +1632,7 @@ v-else v-model:temporary-advices="temporaryAdvices" :patient-info="temporaryPatientInfo" - :billing-medicines="temporaryBillingMedicines" + v-model:billing-medicines="temporaryBillingMedicines" :is-signed-prop="temporarySigned" @submit="handleTemporaryMedicalSubmit" @cancel="handleTemporaryMedicalCancel" diff --git a/healthlink-his-ui/src/views/surgicalschedule/temporaryMedical.vue b/healthlink-his-ui/src/views/surgicalschedule/temporaryMedical.vue index 507047088..d6ab12c74 100755 --- a/healthlink-his-ui/src/views/surgicalschedule/temporaryMedical.vue +++ b/healthlink-his-ui/src/views/surgicalschedule/temporaryMedical.vue @@ -447,7 +447,7 @@ const props = defineProps({ }) // 定义emit事件 -const emit = defineEmits(['submit', 'cancel', 'refresh', 'quote-billing', 'update:temporary-advices']) +const emit = defineEmits(['submit', 'cancel', 'refresh', 'quote-billing', 'update:temporary-advices', 'update:billing-medicines']) // 用户store const userStore = useUserStore() @@ -917,22 +917,114 @@ const handleSignAndSubmit = () => { } } -const handleDeleteAdvice = (index) => { - ElMessageBox.confirm('确定要删除这条医嘱吗?', '提示', { +/** + * 回退已签发医嘱到待签发状态 + * - 不是物理删除,而是将 statusEnum 从 2(ACTIVE) 回退到 1(DRAFT) + * - 清除签发人(signDoctorName)和签发时间(signDate) + * - 回退后刷新,该医嘱将从"已生成"移到"待生成"列表 + */ +const handleDeleteAdvice = async (index) => { + ElMessageBox.confirm('确定要回退这条医嘱吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' - }).then(() => { - // 构建新的临时医嘱数据 + }).then(async () => { + const rollbackAdvice = displayAdvices.value[index] + if (!rollbackAdvice) return + + const originalMedicine = rollbackAdvice.originalMedicine + const originalList = [...displayAdvices.value] const updatedAdvices = [...displayAdvices.value] updatedAdvices.splice(index, 1) - - // 通知父组件更新数据 emit('update:temporary-advices', updatedAdvices) - ElMessage.success('删除成功') + if (originalMedicine && originalMedicine.requestId) { + try { + // 清除 contentJson 中的签发人和签发时间 + let contentData = {} + try { + contentData = typeof originalMedicine.contentJson === 'string' + ? JSON.parse(originalMedicine.contentJson) + : (originalMedicine.contentJson || {}) + } catch (e) { /* ignore */ } + contentData.signDoctorName = '' + contentData.signDate = '' + + const updateItem = { + requestId: originalMedicine.requestId, + dbOpType: '2', // UPDATE(非 DELETE) + adviceType: originalMedicine.adviceType || 1, + encounterId: originalMedicine.encounterId || props.patientInfo.visitId, + patientId: originalMedicine.patientId || props.patientInfo.patientId, + contentJson: JSON.stringify(contentData), + } + // adviceOpType='1'(SAVE) → 后端会设置 statusEnum=DRAFT,完成状态回退 + const res = await savePrescription( + { organizationId: props.patientInfo.orgId || props.patientInfo.organizationId || 1, adviceSaveList: [updateItem] }, + '1' + ) + if (res.code === 200) { + // 回退成功:将药品放回"待生成"列表(保留 requestId,只是状态变了) + if (originalMedicine) { + const restoredItem = { + medicineName: originalMedicine.medicineName || contentData.adviceName || originalMedicine.adviceName || '', + specification: originalMedicine.specification || contentData.volume || '', + quantity: originalMedicine.quantity || contentData.quantity || 1, + batchNumber: originalMedicine.lotNumber || contentData.lotNumber || '', + unitPrice: originalMedicine.unitPrice || contentData.unitPrice || 0, + subtotal: originalMedicine.totalPrice || contentData.totalPrice || 0, + insuranceType: contentData.insuranceType === 1 || originalMedicine.insuranceType === 1 ? '医保' : '自费', + orgId: originalMedicine.orgId || contentData.orgId || props.patientInfo.orgId, + positionId: originalMedicine.positionId || contentData.positionId || props.patientInfo.orgId, + adviceDefinitionId: originalMedicine.adviceDefinitionId || contentData.adviceDefinitionId || null, + adviceTableName: originalMedicine.adviceTableName || contentData.adviceTableName || null, + adviceType: originalMedicine.adviceType || contentData.adviceType || 1, + definitionId: originalMedicine.definitionId || contentData.definitionId || null, + definitionDetailId: originalMedicine.definitionDetailId || contentData.definitionDetailId || null, + requestId: originalMedicine.requestId, // 保留 requestId,记录仍在数据库 + chargeItemId: originalMedicine.chargeItemId || null, + contentJson: JSON.stringify(contentData), // 已清除签名字段 + _signed: false, + } + emit('update:billing-medicines', [...props.billingMedicines, restoredItem]) + } + ElMessage.success('回退成功') + } else { + emit('update:temporary-advices', originalList) + ElMessage.error(res.msg || '回退失败,请重试') + } + } catch (e) { + emit('update:temporary-advices', originalList) + ElMessage.error('回退失败,请重试') + } + } else { + // 未持久化到数据库的医嘱(无 requestId),本地移除即可 + if (originalMedicine) { + const restoredItem = { + medicineName: originalMedicine.medicineName || originalMedicine.adviceName || '', + specification: originalMedicine.specification || '', + quantity: originalMedicine.quantity || 1, + unitPrice: originalMedicine.unitPrice || 0, + subtotal: originalMedicine.totalPrice || 0, + insuranceType: originalMedicine.insuranceType === 1 ? '医保' : '自费', + orgId: originalMedicine.orgId || props.patientInfo.orgId, + positionId: originalMedicine.positionId || props.patientInfo.orgId, + adviceDefinitionId: originalMedicine.adviceDefinitionId || null, + adviceTableName: originalMedicine.adviceTableName || null, + adviceType: originalMedicine.adviceType || 1, + definitionId: originalMedicine.definitionId || null, + definitionDetailId: originalMedicine.definitionDetailId || null, + requestId: null, + chargeItemId: null, + contentJson: originalMedicine.contentJson || null, + _signed: false, + } + emit('update:billing-medicines', [...props.billingMedicines, restoredItem]) + } + ElMessage.success('回退成功') + } }).catch(() => { - // 用户取消删除 + // 用户取消 }) }