Compare commits

...

8 Commits

Author SHA1 Message Date
e8d10befbd fix(#613): 医生端医嘱列表增加退回原因展示列
根因(全链路6环分析):
- ① 前端/页面  医生端医嘱列表无退回原因列 → 无法展示护士填写的退回原因
- ② Controller  不涉及 — 纯转发层
- ③ Service  getRequestBaseInfo() 未填充 reasonText 字段
- ④ Mapper/XML  UNION ALL 查询未选取 back_reason/reason_text 字段
- ⑤ DB  med_medication_request.back_reason 列已存在(上一次修复已迁移)
- ⑥ 关联模块 ⚠️ wor_service_request.reason_text 已存在但未在查询中暴露

修复:
1. RequestBaseDto.java: 新增 reasonText 字段(映射退回原因)
2. DoctorStationAdviceAppMapper.xml: 5 个 UNION ALL 分支各自选取 reason_text
   - med_medication_request → T1.back_reason
   - charge item 回补 → T2.back_reason
   - device_request(2 处)→ NULL(无退回原因字段)
   - wor_service_request → T1.reason_text
3. prescriptionlist.vue: 在诊断列前新增退回原因列

全链路状态流转:
护士端弹窗→输入原因→API传backReason→DB保存→医生端列表展示
                ↑ 本次修复打通最后一环 ↑
2026-05-29 15:53:36 +08:00
e3c0e700a5 chore: 删除误提交的 .bak 文件 2026-05-29 15:04:11 +08:00
a3378b7fbf fix: EncounterDiagnosisMapper selectOne() LIMIT 1 防重复数据报错
根因:getEncounterDiagnosisByEncounterConDefId 使用 selectOne() 查询,
但 SQL 可能返回多条(同就诊同诊断定义多条记录),导致 MyBatis 抛出
'Expected one result but found 2' 异常。

修复:SQL 增加 LIMIT 1,确保最多返回一条。
2026-05-29 15:04:03 +08:00
73df3699ec fix(#613): 补充 DB 迁移 + ServiceRequest 实际写入退回原因
问题:
- med_medication_request 表无 back_reason 列 → Entity 和 Service 写了但 DB 报错
- ServiceRequestServiceImpl.updateDraftStatus 接收 backReason 参数但不使用

修复:
- 新增迁移脚本 sql/迁移记录-DB变更记录/20260529_fix_BUG#613_add_column_back_reason.sql
- 4 个 schema (histest1/histest/hisdev/hisprd) 已执行 ALTER TABLE ADD COLUMN
- ServiceRequestServiceImpl.updateDraftStatus: 新增 setReasonText(backReason)
2026-05-29 14:39:19 +08:00
wangjian963
04dc718555 Merge remote-tracking branch 'origin/develop' into develop 2026-05-29 14:24:46 +08:00
wangjian963
e5a7606229 608
【住院登记-无档登记】登记页面“入院科室”下拉菜单无数据,导致无法完成住院办理
2026-05-29 14:21:56 +08:00
wangjian963
3bdc06d4a7 Merge remote-tracking branch 'origin/develop' into develop 2026-05-29 14:17:30 +08:00
wangjian963
c6ac8d1cb1 565 [库房管理-调拨] 调拨单明细中“源仓库库存数量”未正确读取库存值,始终显示为0
600 【住院护士站-医嘱执行】数据一致性:医嘱执行成功后,在“已执行”列表中无法查询到该医嘱记录
2026-05-29 13:52:27 +08:00
11 changed files with 82 additions and 113 deletions

68
.gitignore vendored
View File

@@ -1,68 +0,0 @@
# 忽略所有编译器、IDE相关的文件
**/.idea/
**/.vscode/
**/*.swp
**/*.swo
**/*.bak
**/*.tmp
**/.vs/
# 忽略 Java 项目编译文件
**/*.class
**/*.jar
**/*.war
**/*.ear
**/target/
**/bin/
# 忽略 Maven、Gradle、Ant 相关文件
**/.mvn/
**/.gradle/
**/build/
**/out/
# 忽略 Eclipse、IntelliJ IDEA 和 NetBeans 临时文件
**/*.log
**/*.project
**/*.classpath
# 忽略 Java 配置文件
**/*.iml
# 忽略 Node.js 和 Vue 项目相关文件
**/node_modules/
**/npm-debug.log
**/yarn-error.log
**/yarn-debug.log
**/dist/
**/*.lock
**/*.tgz
# 忽略 Vue 项目相关构建文件
**/.vuepress/dist/
# 忽略 IDE 配置文件
**/*.launch
**/*.settings/
# 忽略操作系统生成的文件
**/.DS_Store
**/Thumbs.db
**/Desktop.ini
/openhis-miniapp/unpackage
# 忽略设计书
PostgreSQL/openHis_DB设计书.xlsx
public.sql
发版记录/2025-11-12/~$发版日志.docx
发版记录/2025-11-12/~$S-管理系统-调价管理.docx
发版记录/2025-11-12/发版日志.docx
.gitignore
openhis-server-new/openhis-application/src/main/resources/application-dev.yml
.env.test.local
playwright-report/
test-results/

View File

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

View File

@@ -186,12 +186,13 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
QueryWrapper<InpatientAdviceParam> queryWrapper
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
// 手动拼接requestStatus条件COMPLETED(3)时同时包含CHECK_VERIFIED(10)
// 手动拼接requestStatus条件COMPLETED(3)时同时包含CHECK_VERIFIED(10)和PENDING_RECEIVE(11)
// UNION查询外层列名为request_statusT1.status_enum AS request_status不是status_enum
if (requestStatus != null) {
if (RequestStatus.COMPLETED.getValue().equals(requestStatus)) {
queryWrapper.in("request_status",
RequestStatus.COMPLETED.getValue(), RequestStatus.CHECK_VERIFIED.getValue());
RequestStatus.COMPLETED.getValue(), RequestStatus.CHECK_VERIFIED.getValue(),
RequestStatus.PENDING_RECEIVE.getValue());
} else {
queryWrapper.eq("request_status", requestStatus);
}

View File

@@ -262,7 +262,7 @@
AND T1.inventory_status_enum != 3
AND T1.delete_flag = '0'
<choose>
<when test="lotNumber != null">
<when test="lotNumber != null and lotNumber != ''">
AND T1.lot_number = #{lotNumber}
</when>
</choose>

View File

@@ -517,6 +517,7 @@
'med_medication_definition' AS advice_table_name,
T1.medication_id AS advice_definition_id
, T1.content_json::jsonb ->> 'remark' AS remark
, T1.back_reason AS reason_text
FROM med_medication_request AS T1
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
AND T2.delete_flag = '0'
@@ -579,6 +580,7 @@
'med_medication_definition' AS advice_table_name,
T3.ID AS advice_definition_id
, T2.content_json::jsonb ->> 'remark' AS remark
, T2.back_reason AS reason_text
FROM adm_charge_item AS T1
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'
@@ -643,6 +645,7 @@
'adm_device_definition' AS advice_table_name,
CI.product_id AS advice_definition_id
, NULL AS remark
, NULL AS reason_text
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 wor_device_request DR ON DR.id = CI.service_id AND DR.delete_flag = '0'
@@ -698,6 +701,7 @@
'adm_device_definition' AS advice_table_name,
T1.device_def_id AS advice_definition_id
, T1.content_json::jsonb ->> 'remark' AS remark
, NULL AS reason_text
FROM wor_device_request AS T1
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
AND T2.delete_flag = '0'
@@ -755,6 +759,7 @@
'wor_activity_definition' AS advice_table_name,
T1.activity_id AS advice_definition_id,
T1.remark AS remark
, T1.reason_text AS reason_text
FROM wor_service_request AS T1
LEFT JOIN wor_activity_definition AS T2
ON T2.ID = T1.activity_id

View File

@@ -198,8 +198,14 @@ public class ServiceRequestServiceImpl extends ServiceImpl<ServiceRequestMapper,
*/
@Override
public void updateDraftStatus(List<Long> serviceRequestIdList, Long practitionerId, Date checkDate, String backReason) {
baseMapper.update(new ServiceRequest().setStatusEnum(RequestStatus.DRAFT.getValue())
.setPerformerCheckId(SecurityUtils.getLoginUser().getPractitionerId()).setCheckTime(DateUtils.getNowDate()),
ServiceRequest updateEntity = new ServiceRequest()
.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)
.eq(ServiceRequest::getDeleteFlag, DelFlag.NO.getCode()));
}

View File

@@ -27,6 +27,7 @@
AND T1.delete_flag = '0'
AND T2.delete_flag = '0'
AND T1.tenant_id = #{tenantId}
LIMIT 1
</select>
</mapper>

View File

@@ -1345,6 +1345,18 @@
</span>
</template>
</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
label="诊断"
align="center"

View File

@@ -506,21 +506,10 @@ function getInitOptions() {
const wardPromise = getPractitionerWard();
Promise.all([orgPromise, wardPromise]).then(([orgRes, wardRes]) => {
const allOrgs = orgRes.data.records.filter(
// 入院科室:展示所有 typeEnum=2(科室) + classEnum含"2"(住院) 的科室
organization.value = orgRes.data.records.filter(
(record) => record.typeEnum === 2 && checkClassEnumValue(record.classEnum, 2)
);
const allWards = wardRes.data || [];
// 提取所有病区关联的科室ID
const linkedOrgIds = new Set();
allWards.forEach((ward) => {
if (ward.organizationId) {
linkedOrgIds.add(ward.organizationId);
}
});
// 过滤出与病区关联过的科室
organization.value = allOrgs.filter((org) => linkedOrgIds.has(org.id));
// Bug #178 Fix: 如果已选科室不在列表中,手动添加以确保正确显示
const selectedOrgId = props.inHospitalInfo?.inHospitalOrgId;

View File

@@ -1180,6 +1180,9 @@ function selectRow(rowValue, index) {
form.purchaseinventoryList[index].partPercent = rowValue.partPercent;
form.purchaseinventoryList[index].unitList = rowValue.unitList[0];
form.purchaseinventoryList[index].lotNumber = rowValue.lotNumber;
// 补全单位字典文本formatInventory 依赖此字段格式化库存显示
form.purchaseinventoryList[index].unitCode_dictText = rowValue.unitCode_dictText;
form.purchaseinventoryList[index].minUnitCode_dictText = rowValue.minUnitCode_dictText;
form.purchaseinventoryList[index].itemQuantity = 0;
form.purchaseinventoryList[index].totalPrice = 0;
// 维护一个大小单位的map用来判断当前选中单位是大/小单位
@@ -1194,21 +1197,13 @@ function selectRow(rowValue, index) {
value: rowValue.minUnitCode,
},
];
if (route.query.supplyBusNo) {
handleLocationClick(
receiptHeaderForm.sourceLocationId1,
receiptHeaderForm.purposeLocationId1,
form.purchaseinventoryList[index].itemId,
index
);
} else {
handleLocationClick(
form.purchaseinventoryList[index].sourceLocationId,
form.purchaseinventoryList[index].purposeLocationId,
form.purchaseinventoryList[index].itemId,
index
);
}
// 新单/编辑单统一使用行级仓库ID不再分支判断 route.query.supplyBusNo
handleLocationClick(
form.purchaseinventoryList[index].sourceLocationId,
form.purchaseinventoryList[index].purposeLocationId,
form.purchaseinventoryList[index].itemId,
index
);
editBatchTransfer(index); // todo
}
@@ -1220,23 +1215,29 @@ function handleLocationClick(id, purposeLocationId, itemId, index) {
objLocationId: purposeLocationId,
lotNumber: form.purchaseinventoryList[index].lotNumber,
}).then((res) => {
if (res.data && res.data[0]) {
form.purchaseinventoryList[index].itemTable = res.data[0].itemTable || '';
form.purchaseinventoryList[index].supplierId = res.data[0].supplierId || '';
form.purchaseinventoryList[index].startTime = formatDateymd(res.data[0].productionDate) || '';
form.purchaseinventoryList[index].endTime = formatDateymd(res.data[0].expirationDate) || '';
if (res.data && res.data.length) {
// SQL 按 locationId 分组后可能有两条记录(源/目的),根据 locationId 精确匹配而非盲目取 res.data[0]
const srcId = String(id);
const purId = String(purposeLocationId);
const sourceRow = res.data.find(item => String(item.locationId) === srcId) || {};
const purposeRow = res.data.find(item => String(item.locationId) === purId) || {};
form.purchaseinventoryList[index].price = res.data[0].price;
form.purchaseinventoryList[index].totalSourceQuantity = res.data[0].orgQuantity;
form.purchaseinventoryList[index].totalPurposeQuantity = res.data[0].objQuantity;
form.purchaseinventoryList[index].itemTable = sourceRow.itemTable || '';
form.purchaseinventoryList[index].supplierId = sourceRow.supplierId || '';
form.purchaseinventoryList[index].startTime = formatDateymd(sourceRow.productionDate) || '';
form.purchaseinventoryList[index].endTime = formatDateymd(sourceRow.expirationDate) || '';
form.purchaseinventoryList[index].price = sourceRow.price;
form.purchaseinventoryList[index].totalSourceQuantity = sourceRow.orgQuantity || 0;
form.purchaseinventoryList[index].totalPurposeQuantity = purposeRow.objQuantity || 0;
form.purchaseinventoryList[index].totalSourceQuantityDisplay = formatInventory(
res.data[0].orgQuantity,
sourceRow.orgQuantity || 0,
form.purchaseinventoryList[index].partPercent,
form.purchaseinventoryList[index].unitCode_dictText,
form.purchaseinventoryList[index].minUnitCode_dictText
);
form.purchaseinventoryList[index].totalPurposeQuantityDisplay = formatInventory(
res.data[0].objQuantity,
purposeRow.objQuantity || 0,
form.purchaseinventoryList[index].partPercent,
form.purchaseinventoryList[index].unitCode_dictText,
form.purchaseinventoryList[index].minUnitCode_dictText
@@ -1340,8 +1341,8 @@ function formatInventory(quantity, partPercent, unitCode, minUnitCode) {
return isNegative ? '-' + result : result;
}
// 整除情况
const result = absQuantity / partPercent;
// 整除时也需拼接单位后缀,否则显示为纯数字缺少单位信息
const result = (absQuantity / partPercent) + ' ' + unitCode;
return isNegative ? '-' + result : result;
}

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
$$;