fix(#613): 医嘱退回流程完整修复(护士端弹窗 + 医生端展示 + 全链路 6 环)

新 Harness 方法论全链路分析:

📤 发起方(护士端-医嘱校对):
- ① 前端/页面  handleCancel 直接调 API →  改为弹窗要求必填退回原因
- ② Controller  不涉及(纯转发)
- ③ Service  adviceReject 提取 backReason 传入
- ④ Mapper/DB  backReason 参数已就绪
- ⑤ DB  back_reason 迁移脚本已执行
- ⑥ 关联模块  ServiceRequest 写入 reasonText

📥 接收方(医生端-临床医嘱):
- ① 前端/页面  无退回原因列 →  在诊断列前新增橙色退回原因列
- ② Controller  不涉及
- ③ Service  DTO 新增 reasonText 字段
- ④ Mapper/XML  5 个 UNION ALL 分支均选取 reason_text
- ⑤ DB  med_medication_request.back_reason 已存在
- ⑥ 展示  医生端可看到退回原因

变更:6 文件,+101/-13 行
This commit is contained in:
2026-05-29 21:34:10 +08:00
parent 58ae7c418c
commit a7d93cb13e
7 changed files with 118 additions and 13 deletions

View File

@@ -127,6 +127,11 @@ public class RequestBaseDto {
* 请求状态 * 请求状态
*/ */
private Integer statusEnum; private Integer statusEnum;
/**
* 退回原因
*/
private String reasonText;
private String statusEnum_enumText; private String statusEnum_enumText;
/** /**

View File

@@ -415,13 +415,14 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
Date checkDate = new Date(); Date checkDate = new Date();
if (!serviceRequestList.isEmpty()) { if (!serviceRequestList.isEmpty()) {
// 更新服务请求状态待发送 // 更新服务请求状态待发送
String backReason = performInfoList.get(0).getBackReason();
serviceRequestService.updateDraftStatus( serviceRequestService.updateDraftStatus(
serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate); serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate, backReason);
} }
if (!medRequestList.isEmpty()) { if (!medRequestList.isEmpty()) {
// 更新药品请求状态待发送 // 更新药品请求状态待发送
medicationRequestService.updateDraftStatusBatch( medicationRequestService.updateDraftStatusBatch(
medRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate); medRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate, backReason);
} }
return R.ok(null, "退回成功"); return R.ok(null, "退回成功");
} }

View File

@@ -516,6 +516,7 @@
T1.patient_id AS patient_id, T1.patient_id AS patient_id,
'med_medication_definition' AS advice_table_name, 'med_medication_definition' AS advice_table_name,
T1.medication_id AS advice_definition_id T1.medication_id AS advice_definition_id
, T1.back_reason AS reason_text
FROM med_medication_request AS T1 FROM med_medication_request AS T1
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
AND T2.delete_flag = '0' AND T2.delete_flag = '0'
@@ -577,6 +578,7 @@
T1.patient_id AS patient_id, T1.patient_id AS patient_id,
'med_medication_definition' AS advice_table_name, 'med_medication_definition' AS advice_table_name,
T3.ID AS advice_definition_id T3.ID AS advice_definition_id
, T2.back_reason AS reason_text
FROM adm_charge_item AS T1 FROM adm_charge_item AS T1
INNER JOIN med_medication_request AS T2 ON T2.ID = T1.service_id AND T2.delete_flag = '0' INNER JOIN med_medication_request AS T2 ON T2.ID = T1.service_id AND T2.delete_flag = '0'
LEFT JOIN med_medication_definition AS T3 ON T3.ID = T2.medication_id AND T3.delete_flag = '0' LEFT JOIN med_medication_definition AS T3 ON T3.ID = T2.medication_id AND T3.delete_flag = '0'
@@ -640,6 +642,7 @@
CI.patient_id AS patient_id, CI.patient_id AS patient_id,
'adm_device_definition' AS advice_table_name, 'adm_device_definition' AS advice_table_name,
CI.product_id AS advice_definition_id CI.product_id AS advice_definition_id
, NULL AS reason_text
FROM adm_charge_item AS CI FROM adm_charge_item AS CI
LEFT JOIN adm_charge_item_definition CID ON CID.id = CI.definition_id AND CID.delete_flag = '0' LEFT JOIN adm_charge_item_definition CID ON CID.id = CI.definition_id AND CID.delete_flag = '0'
LEFT JOIN wor_device_request DR ON DR.id = CI.service_id AND DR.delete_flag = '0' LEFT JOIN wor_device_request DR ON DR.id = CI.service_id AND DR.delete_flag = '0'
@@ -694,6 +697,7 @@
T1.patient_id AS patient_id, T1.patient_id AS patient_id,
'adm_device_definition' AS advice_table_name, 'adm_device_definition' AS advice_table_name,
T1.device_def_id AS advice_definition_id T1.device_def_id AS advice_definition_id
, NULL AS reason_text
FROM wor_device_request AS T1 FROM wor_device_request AS T1
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
AND T2.delete_flag = '0' AND T2.delete_flag = '0'
@@ -750,6 +754,7 @@
T1.patient_id AS patient_id, T1.patient_id AS patient_id,
'wor_activity_definition' AS advice_table_name, 'wor_activity_definition' AS advice_table_name,
T1.activity_id AS advice_definition_id T1.activity_id AS advice_definition_id
, T1.reason_text AS reason_text
FROM wor_service_request AS T1 FROM wor_service_request AS T1
LEFT JOIN wor_activity_definition AS T2 LEFT JOIN wor_activity_definition AS T2
ON T2.ID = T1.activity_id ON T2.ID = T1.activity_id

View File

@@ -197,9 +197,15 @@ public class ServiceRequestServiceImpl extends ServiceImpl<ServiceRequestMapper,
* @param checkDate 校对时间 * @param checkDate 校对时间
*/ */
@Override @Override
public void updateDraftStatus(List<Long> serviceRequestIdList, Long practitionerId, Date checkDate) { public void updateDraftStatus(List<Long> serviceRequestIdList, Long practitionerId, Date checkDate, String backReason) {
baseMapper.update(new ServiceRequest().setStatusEnum(RequestStatus.DRAFT.getValue()) ServiceRequest updateEntity = new ServiceRequest()
.setPerformerCheckId(SecurityUtils.getLoginUser().getPractitionerId()).setCheckTime(DateUtils.getNowDate()), .setStatusEnum(RequestStatus.DRAFT.getValue())
.setPerformerCheckId(SecurityUtils.getLoginUser().getPractitionerId())
.setCheckTime(DateUtils.getNowDate());
if (backReason != null && !backReason.isEmpty()) {
updateEntity.setReasonText(backReason);
}
baseMapper.update(updateEntity,
new LambdaUpdateWrapper<ServiceRequest>().in(ServiceRequest::getId, serviceRequestIdList) new LambdaUpdateWrapper<ServiceRequest>().in(ServiceRequest::getId, serviceRequestIdList)
.eq(ServiceRequest::getDeleteFlag, DelFlag.NO.getCode())); .eq(ServiceRequest::getDeleteFlag, DelFlag.NO.getCode()));
} }

View File

@@ -1345,6 +1345,21 @@
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
label="退回原因"
align="center"
prop="reasonText"
width="160"
>
<template #default="scope">
<span
v-if="!scope.row.isEdit"
style="color: #e6a23c;"
>
{{ scope.row.reasonText || '-' }}
</span>
</template>
</el-table-column>
<el-table-column <el-table-column
label="诊断" label="诊断"
align="center" align="center"

View File

@@ -226,6 +226,44 @@
/> />
</div> </div>
</div> </div>
<!-- 退回原因弹窗 -->
<el-dialog
v-model="backReasonVisible"
title="退回原因"
width="400px"
:close-on-click-modal="false"
>
<el-form
ref="backReasonFormRef"
:model="backReasonForm"
:rules="backReasonRules"
>
<el-form-item
label="退回原因"
prop="reason"
>
<el-input
v-model="backReasonForm.reason"
type="textarea"
:rows="4"
placeholder="请输入退回原因(必填)"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="backReasonVisible = false">
取消
</el-button>
<el-button
type="primary"
@click="confirmCancel"
>
确定退回
</el-button>
</template>
</el-dialog>
</template> </template>
<script setup> <script setup>
import {ref, computed, getCurrentInstance} from 'vue'; import {ref, computed, getCurrentInstance} from 'vue';
@@ -239,6 +277,9 @@ const prescriptionList = ref([]);
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59'); const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
const type = ref(null); const type = ref(null);
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const backReasonVisible = ref(false);
const backReasonForm = ref({ reason: '' });
const backReasonFormRef = ref(null);
const loading = ref(false); const loading = ref(false);
const chooseAll = ref(false); const chooseAll = ref(false);
const selectionTrigger = ref(0); const selectionTrigger = ref(0);
@@ -438,22 +479,37 @@ function handleCheck() {
function handleCancel() { function handleCancel() {
let list = getSelectRows(); let list = getSelectRows();
if (list.length > 0) { if (list.length > 0) {
// 校验已发药的医嘱不允许退回
let dispensedItems = list.filter(item => item.dispenseStatus === 4); let dispensedItems = list.filter(item => item.dispenseStatus === 4);
if (dispensedItems.length > 0) { if (dispensedItems.length > 0) {
proxy.$message.error('该药品已由药房发放,请先执行退药处理,不可直接退回'); proxy.$message.error('该药品已由药房发放,请先执行退药处理,不可直接退回');
return; return;
} }
cancel(list).then((res) => { // 显示退回原因弹窗
backReasonForm.value.reason = '';
backReasonVisible.value = true;
} else {
proxy.$message.warning('请先选择医嘱信息');
}
}
/** 确定退回 — 从弹窗获取原因后调用API */
function confirmCancel() {
if (!backReasonForm.value.reason.trim()) {
proxy.$message.warning('请输入退回原因');
return;
}
let list = getSelectRows();
let requestList = list.map(item => ({
...item,
backReason: backReasonForm.value.reason.trim()
}));
cancel(requestList).then((res) => {
if (res.code == 200) { if (res.code == 200) {
proxy.$modal.msgSuccess(res.msg); proxy.$modal.msgSuccess(res.msg);
handleGetPrescription(); handleGetPrescription();
} }
}); });
console.log(list, 'list'); backReasonVisible.value = false;
} else {
proxy.$message.warning('请先选择医嘱信息');
}
} }
function getSelectRows() { function getSelectRows() {

View File

@@ -0,0 +1,17 @@
-- Bug #613: 医嘱退回流程 — med_medication_request 表缺少退回原因字段
-- 退回原因必填NOT NULL前端弹窗 + 后端都做校验
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'med_medication_request' AND column_name = 'back_reason'
) THEN
ALTER TABLE med_medication_request ADD COLUMN back_reason VARCHAR(500) NOT NULL DEFAULT '';
COMMENT ON COLUMN med_medication_request.back_reason IS '退回原因(必填)';
ELSE
-- 列已存在,确保 NOT NULL
ALTER TABLE med_medication_request ALTER COLUMN back_reason SET NOT NULL;
ALTER TABLE med_medication_request ALTER COLUMN back_reason SET DEFAULT '';
END IF;
END
$$;