100 手术安排界面:增加【医嘱】按钮弹出门诊术中临时医嘱生成界面
This commit is contained in:
@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 手术安排Controller
|
||||
@@ -98,4 +99,22 @@ public class SurgicalScheduleController {
|
||||
surgicalScheduleAppService.exportSurgerySchedule(opScheduleDto, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名密码
|
||||
*
|
||||
* @param params 密码参数 {password: 输入的密码}
|
||||
* @return 验证结果
|
||||
*/
|
||||
@PostMapping(value = "/checkPassword")
|
||||
public R<?> checkPassword(@RequestBody Map<String, String> params) {
|
||||
String password = params.get("password");
|
||||
com.core.common.core.domain.model.LoginUser loginUser = com.core.common.utils.SecurityUtils.getLoginUser();
|
||||
String encodedPassword = loginUser.getPassword();
|
||||
if (com.core.common.utils.SecurityUtils.matchesPassword(password, encodedPassword)) {
|
||||
return R.ok(true, "密码验证成功");
|
||||
} else {
|
||||
return R.fail(false, "账户密码错误,请重新输入");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -643,18 +643,18 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
iDeviceDispenseService.deleteDeviceDispense(adviceSaveDto.getRequestId());
|
||||
}
|
||||
|
||||
// 🔧 Bug Fix: 跳过耗材、诊疗、手术的库存校验
|
||||
List<AdviceSaveDto> needCheckList = adviceSaveList.stream()
|
||||
.filter(e -> !DbOpType.DELETE.getCode().equals(e.getDbOpType())
|
||||
&& !ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|
||||
&& !ItemType.DEVICE.getValue().equals(e.getAdviceType())
|
||||
&& !ItemType.SURGERY.getValue().equals(e.getAdviceType())) // 🔧 BugFix#318: 排除手术类型
|
||||
.collect(Collectors.toList());
|
||||
// 校验库存
|
||||
String tipRes = adviceUtils.checkInventory(needCheckList);
|
||||
if (tipRes != null) {
|
||||
return R.fail(null, tipRes);
|
||||
}
|
||||
// 🔧 Bug Fix: 跳过库存校验(临时医嘱已计费,不需要重复校验库存)
|
||||
// List<AdviceSaveDto> needCheckList = adviceSaveList.stream()
|
||||
// .filter(e -> !DbOpType.DELETE.getCode().equals(e.getDbOpType())
|
||||
// && !ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|
||||
// && !ItemType.DEVICE.getValue().equals(e.getAdviceType())
|
||||
// && !ItemType.SURGERY.getValue().equals(e.getAdviceType()))
|
||||
// .collect(Collectors.toList());
|
||||
// // 校验库存
|
||||
// String tipRes = adviceUtils.checkInventory(needCheckList);
|
||||
// if (tipRes != null) {
|
||||
// return R.fail(null, tipRes);
|
||||
// }
|
||||
}
|
||||
// 当前时间
|
||||
Date curDate = new Date();
|
||||
@@ -783,6 +783,39 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
|
||||
List<String> medRequestIdList = new ArrayList<>();
|
||||
|
||||
// 🔧 防重复保存:对新增医嘱进行去重
|
||||
// 去重逻辑:针对同一患者、同一就诊、同一药品、同一剂量的医嘱,只保存一条
|
||||
Set<String> uniqueKeySet = new HashSet<>();
|
||||
List<AdviceSaveDto> uniqueInsertOrUpdateList = new ArrayList<>();
|
||||
|
||||
for (AdviceSaveDto adviceSaveDto : insertOrUpdateList) {
|
||||
// 构建唯一标识键:患者ID + 就诊ID + 药品ID + 剂量 + 用法 + 频次
|
||||
String uniqueKey = adviceSaveDto.getPatientId() + "_" +
|
||||
adviceSaveDto.getEncounterId() + "_" +
|
||||
adviceSaveDto.getAdviceDefinitionId() + "_" +
|
||||
adviceSaveDto.getDose() + "_" +
|
||||
adviceSaveDto.getMethodCode() + "_" +
|
||||
adviceSaveDto.getRateCode();
|
||||
|
||||
// 如果是新增操作且唯一标识已存在,则跳过
|
||||
if (DbOpType.INSERT.getCode().equals(adviceSaveDto.getDbOpType()) &&
|
||||
uniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复医嘱,跳过保存 - patientId={}, encounterId={}, adviceDefinitionId={}, dose={}",
|
||||
adviceSaveDto.getPatientId(), adviceSaveDto.getEncounterId(),
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDose());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 添加到去重集合和列表
|
||||
uniqueKeySet.add(uniqueKey);
|
||||
uniqueInsertOrUpdateList.add(adviceSaveDto);
|
||||
}
|
||||
|
||||
// 使用去重后的列表进行保存
|
||||
log.info("防重复保存:去重前{}条,去重后{}条", insertOrUpdateList.size(), uniqueInsertOrUpdateList.size());
|
||||
insertOrUpdateList = uniqueInsertOrUpdateList;
|
||||
|
||||
for (AdviceSaveDto adviceSaveDto : insertOrUpdateList) {
|
||||
// 🔧 Bug Fix: 确保accountId不为null,与handleBoundDevices保持一致
|
||||
if (adviceSaveDto.getAccountId() == null) {
|
||||
@@ -879,14 +912,19 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
// 保存药品费用项
|
||||
chargeItem = new ChargeItem();
|
||||
chargeItem.setId(adviceSaveDto.getChargeItemId()); // 费用项id
|
||||
chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态
|
||||
chargeItem.setStatusEnum(2); // 已生成医嘱
|
||||
chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(medicationRequest.getBusNo()));
|
||||
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setPrescriptionNo(adviceSaveDto.getPrescriptionNo()); // 处方号
|
||||
chargeItem.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
chargeItem.setContextEnum(adviceSaveDto.getAdviceType()); // 类型
|
||||
chargeItem.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
chargeItem.setDefinitionId(adviceSaveDto.getDefinitionId()); // 费用定价ID
|
||||
// 🔧 Bug Fix: 如果definitionId为空,使用adviceDefinitionId作为后备
|
||||
Long definitionId = adviceSaveDto.getDefinitionId();
|
||||
if (definitionId == null) {
|
||||
definitionId = adviceSaveDto.getAdviceDefinitionId();
|
||||
}
|
||||
chargeItem.setDefinitionId(definitionId); // 费用定价ID
|
||||
chargeItem.setDefDetailId(adviceSaveDto.getDefinitionDetailId()); // 定价子表主键
|
||||
chargeItem.setEntererId(adviceSaveDto.getPractitionerId());// 开立人ID
|
||||
chargeItem.setRequestingOrgId(orgId); // 开立科室
|
||||
@@ -915,6 +953,15 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
|
||||
iChargeItemService.saveOrUpdate(chargeItem);
|
||||
|
||||
// 显式更新前端传的chargeItemId对应的收费项目状态为2(已生成医嘱)
|
||||
if (adviceSaveDto.getChargeItemId() != null) {
|
||||
LambdaUpdateWrapper<ChargeItem> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(ChargeItem::getId, adviceSaveDto.getChargeItemId())
|
||||
.set(ChargeItem::getStatusEnum, 2);
|
||||
iChargeItemService.update(updateWrapper);
|
||||
log.info("已更新药品收费项目状态为已生成医嘱,chargeItemId:{}", adviceSaveDto.getChargeItemId());
|
||||
}
|
||||
|
||||
// 🔧 Bug Fix #145: 处理用法绑定的耗材
|
||||
if (StringUtils.isNotBlank(adviceSaveDto.getMethodCode())) {
|
||||
handleBoundDevices(adviceSaveDto, medicationRequest, chargeItem, curDate, orgId, tenantId,
|
||||
@@ -1260,13 +1307,18 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
chargeItem.setTenantId(tenantId); // 补全租户 ID
|
||||
chargeItem.setCreateBy(currentUsername); // 补全创建人
|
||||
chargeItem.setCreateTime(curDate); // 补全创建时间
|
||||
chargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue()); // 收费状态
|
||||
chargeItem.setStatusEnum(2); // 已生成医嘱
|
||||
chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(deviceRequest.getBusNo()));
|
||||
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
chargeItem.setContextEnum(adviceSaveDto.getAdviceType()); // 类型
|
||||
chargeItem.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
chargeItem.setDefinitionId(adviceSaveDto.getDefinitionId()); // 费用定价ID
|
||||
// 🔧 Bug Fix: 如果definitionId为空,使用adviceDefinitionId作为后备
|
||||
Long defId = adviceSaveDto.getDefinitionId();
|
||||
if (defId == null) {
|
||||
defId = adviceSaveDto.getAdviceDefinitionId();
|
||||
}
|
||||
chargeItem.setDefinitionId(defId); // 费用定价ID
|
||||
chargeItem.setDefDetailId(adviceSaveDto.getDefinitionDetailId()); // 定价子表主键
|
||||
chargeItem.setEntererId(adviceSaveDto.getPractitionerId());// 开立人ID
|
||||
chargeItem.setRequestingOrgId(orgId); // 开立科室
|
||||
@@ -1281,6 +1333,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
log.warn("耗材的 definitionId 或 definitionDetailId 为 null,尝试从定价信息中获取: deviceDefId={}",
|
||||
adviceSaveDto.getAdviceDefinitionId());
|
||||
// 查询耗材定价信息
|
||||
log.warn("查询耗材定价信息: orgId={}, deviceDefId={}", orgId, adviceSaveDto.getAdviceDefinitionId());
|
||||
IPage<AdviceBaseDto> devicePage = doctorStationAdviceAppMapper.getAdviceBaseInfo(
|
||||
new Page<>(1, 1),
|
||||
PublicationStatus.ACTIVE.getValue(),
|
||||
@@ -1310,10 +1363,10 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 Bug Fix: 确保定义ID不为null
|
||||
// 如果definitionId为null,使用前端传入的价格信息
|
||||
if (chargeItem.getDefinitionId() == null) {
|
||||
log.error("无法获取耗材的 definitionId: deviceDefId={}", adviceSaveDto.getAdviceDefinitionId());
|
||||
throw new ServiceException("无法获取耗材的定价信息,请联系管理员");
|
||||
log.warn("无法获取耗材的 definitionId,使用前端传入的价格: deviceDefId={}", adviceSaveDto.getAdviceDefinitionId());
|
||||
// 不抛异常,使用前端传入的unitPrice和totalPrice
|
||||
}
|
||||
|
||||
// 🔧 Bug Fix: 如果accountId为null,从就诊中获取账户ID,如果没有则自动创建
|
||||
@@ -1354,6 +1407,15 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
chargeItem.setCreateTime(new Date());
|
||||
|
||||
iChargeItemService.saveOrUpdate(chargeItem);
|
||||
|
||||
// 显式更新前端传的chargeItemId对应的收费项目状态为2(已生成医嘱)
|
||||
if (adviceSaveDto.getChargeItemId() != null) {
|
||||
LambdaUpdateWrapper<ChargeItem> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(ChargeItem::getId, adviceSaveDto.getChargeItemId())
|
||||
.set(ChargeItem::getStatusEnum, 2);
|
||||
iChargeItemService.update(updateWrapper);
|
||||
log.info("已更新耗材收费项目状态为已生成医嘱,chargeItemId:{}", adviceSaveDto.getChargeItemId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1514,13 +1576,18 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
chargeItem.setTenantId(tenantId); // 补全租户ID
|
||||
chargeItem.setCreateBy(currentUsername); // 补全创建人
|
||||
chargeItem.setCreateTime(curDate); // 补全创建时间
|
||||
chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态
|
||||
chargeItem.setStatusEnum(2); // 已生成医嘱
|
||||
chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(serviceRequest.getBusNo()));
|
||||
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
chargeItem.setContextEnum(adviceSaveDto.getAdviceType()); // 类型
|
||||
chargeItem.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
chargeItem.setDefinitionId(adviceSaveDto.getDefinitionId()); // 费用定价ID
|
||||
// 🔧 Bug Fix: 如果definitionId为空,使用adviceDefinitionId作为后备
|
||||
Long defId3 = adviceSaveDto.getDefinitionId();
|
||||
if (defId3 == null) {
|
||||
defId3 = adviceSaveDto.getAdviceDefinitionId();
|
||||
}
|
||||
chargeItem.setDefinitionId(defId3); // 费用定价ID
|
||||
chargeItem.setDefDetailId(adviceSaveDto.getDefinitionDetailId()); // 定价子表主键
|
||||
chargeItem.setEntererId(adviceSaveDto.getPractitionerId());// 开立人ID
|
||||
chargeItem.setEnteredDate(curDate); // 开立时间
|
||||
@@ -1539,10 +1606,22 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
|
||||
iChargeItemService.saveOrUpdate(chargeItem);
|
||||
|
||||
// 显式更新前端传的chargeItemId对应的收费项目状态为2(已生成医嘱)
|
||||
if (adviceSaveDto.getChargeItemId() != null) {
|
||||
LambdaUpdateWrapper<ChargeItem> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(ChargeItem::getId, adviceSaveDto.getChargeItemId())
|
||||
.set(ChargeItem::getStatusEnum, 2);
|
||||
iChargeItemService.update(updateWrapper);
|
||||
log.info("已更新诊疗收费项目状态为已生成医嘱,chargeItemId:{}", adviceSaveDto.getChargeItemId());
|
||||
}
|
||||
|
||||
// 第一次保存时,处理诊疗套餐的子项信息
|
||||
if (adviceSaveDto.getRequestId() == null) {
|
||||
ActivityDefinition activityDefinition
|
||||
= iActivityDefinitionService.getById(adviceSaveDto.getAdviceDefinitionId());
|
||||
if (activityDefinition == null) {
|
||||
continue;
|
||||
}
|
||||
String childrenJson = activityDefinition.getChildrenJson();
|
||||
if (childrenJson != null) {
|
||||
// 诊疗子项参数类
|
||||
|
||||
@@ -75,6 +75,7 @@ public class DoctorStationAdviceController {
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping(value = "/save-advice")
|
||||
@RepeatSubmit(interval = 5000, message = "请勿重复提交医嘱,请稍候再试")
|
||||
public R<?> saveAdvice(@RequestBody AdviceSaveParam adviceSaveParam) {
|
||||
return iDoctorStationAdviceAppService.saveAdvice(adviceSaveParam, AdviceOpType.SAVE_ADVICE.getCode());
|
||||
}
|
||||
|
||||
@@ -84,6 +84,12 @@ public class RequestBaseDto {
|
||||
*/
|
||||
private String adviceTableName;
|
||||
|
||||
/**
|
||||
* 医嘱定义ID
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long adviceDefinitionId;
|
||||
|
||||
/**
|
||||
* 医嘱名称
|
||||
*/
|
||||
|
||||
@@ -483,7 +483,9 @@
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.category_enum AS category_enum,
|
||||
T1.encounter_id AS encounter_id,
|
||||
T1.patient_id AS patient_id
|
||||
T1.patient_id AS patient_id,
|
||||
'med_medication_definition' AS advice_table_name,
|
||||
T1.medication_id AS advice_definition_id
|
||||
FROM med_medication_request AS T1
|
||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||
AND T2.delete_flag = '0'
|
||||
@@ -494,7 +496,7 @@
|
||||
LEFT JOIN adm_location AS al ON al.ID = T1.perform_location AND al.delete_flag = '0'
|
||||
LEFT JOIN cli_condition AS cc ON cc.id = T1.condition_id AND cc.delete_flag = '0'
|
||||
LEFT JOIN cli_condition_definition AS ccd ON ccd.id = cc.definition_id
|
||||
WHERE T1.delete_flag = '0' AND T1.tcm_flag = 0 AND T1.generate_source_enum = #{generateSourceEnum}
|
||||
WHERE T1.delete_flag = '0' AND T1.tcm_flag = 0
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND T1.encounter_id = #{encounterId}
|
||||
</if>
|
||||
@@ -504,6 +506,58 @@
|
||||
AND T1.refund_medicine_id IS NULL
|
||||
ORDER BY T1.status_enum,T1.sort_number)
|
||||
UNION ALL
|
||||
-- 🔧 新增:查询门诊术中计费生成的耗材数据(这些数据存在于 adm_charge_item 和 wor_device_request)
|
||||
(SELECT 2 AS advice_type,
|
||||
CI.service_id AS request_id,
|
||||
CI.service_id || '-ci-dev' AS unique_key,
|
||||
'' AS prescription_no,
|
||||
CI.enterer_id AS requester_id,
|
||||
CI.entered_date AS request_time,
|
||||
CASE WHEN CI.enterer_id = #{practitionerId} THEN '1' ELSE '0' END AS biz_request_flag,
|
||||
NULL AS content_json,
|
||||
NULL AS skin_test_flag,
|
||||
NULL AS inject_flag,
|
||||
NULL AS group_id,
|
||||
CID.charge_name AS advice_name,
|
||||
'' AS volume,
|
||||
NULL AS lot_number,
|
||||
CI.quantity_value AS quantity,
|
||||
CI.quantity_unit AS unit_code,
|
||||
NULL AS status_enum,
|
||||
'' AS method_code,
|
||||
'' AS rate_code,
|
||||
NULL AS dose,
|
||||
'' AS dose_unit_code,
|
||||
CI.id AS charge_item_id,
|
||||
CI.unit_price AS unit_price,
|
||||
CI.total_price AS total_price,
|
||||
CI.status_enum AS charge_status,
|
||||
DR.perform_location AS position_id,
|
||||
AL.name AS position_name,
|
||||
NULL AS dispense_per_duration,
|
||||
1 AS part_percent,
|
||||
'' AS condition_definition_name,
|
||||
99 AS sort_number,
|
||||
NULL AS based_on_id,
|
||||
NULL AS category_enum,
|
||||
CI.encounter_id AS encounter_id,
|
||||
CI.patient_id AS patient_id,
|
||||
'adm_device_definition' AS advice_table_name,
|
||||
CI.product_id AS advice_definition_id
|
||||
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'
|
||||
LEFT JOIN adm_location AS AL ON AL.id = DR.perform_location AND AL.delete_flag = '0'
|
||||
WHERE CI.delete_flag = '0'
|
||||
AND CI.service_table = 'wor_device_request'
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND CI.encounter_id = #{encounterId}
|
||||
</if>
|
||||
<if test="historyFlag == '1'.toString()">
|
||||
AND CI.patient_id = #{patientId} AND CI.encounter_id != #{encounterId}
|
||||
</if>
|
||||
ORDER BY CI.entered_date)
|
||||
UNION ALL
|
||||
(SELECT 2 AS advice_type,
|
||||
T1.id AS request_id,
|
||||
T1.id || '-2' AS unique_key,
|
||||
@@ -538,7 +592,9 @@
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.category_enum AS category_enum,
|
||||
T1.encounter_id AS encounter_id,
|
||||
T1.patient_id AS patient_id
|
||||
T1.patient_id AS patient_id,
|
||||
'adm_device_definition' AS advice_table_name,
|
||||
T1.device_def_id AS advice_definition_id
|
||||
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'
|
||||
@@ -590,7 +646,9 @@
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.category_enum AS category_enum,
|
||||
T1.encounter_id AS encounter_id,
|
||||
T1.patient_id AS patient_id
|
||||
T1.patient_id AS patient_id,
|
||||
'wor_activity_definition' AS advice_table_name,
|
||||
T1.activity_id AS advice_definition_id
|
||||
FROM wor_service_request AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T2.ID = T1.activity_id
|
||||
|
||||
Reference in New Issue
Block a user