12 Commits

Author SHA1 Message Date
5b0acede89 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue
2026-04-01 18:27:31 +08:00
ac1cd3afc8 fix(prescription): 解决处方列表中手术类型和其他医嘱类型的问题
- 更新 lodash.template 修复脚本以处理 assignWith 函数的自定义器参数
- 在多个处方组件中引入 drord_doctor_type 字典用于动态生成医嘱类型列表
- 修复手术类型(adviceType=6)的特殊处理逻辑,包括类型映射和字段过滤
- 调整后端医嘱保存服务中的类型分类逻辑,正确处理手术类型
- 更新数据库查询映射以支持手术类型的正确显示和数据传输
- 修复费用对话框和订单表单中的相关类型显示问题
2026-04-01 18:24:24 +08:00
6694ae52ba feat: 手术申请列表-手术单号移到申请日期之前(第一栏) 2026-04-01 14:00:58 +08:00
9491ceaa5d feat: 手术申请列表-手术单号支持点击查看详情 2026-04-01 13:36:44 +08:00
db9a70a99d feat: 手术申请列表-手术单号放第二栏并支持点击查看详情 2026-04-01 13:28:24 +08:00
b1d6c6008e fix: doctorstation手术医嘱advice_type使用category_enum,advice_name支持surgeryName 2026-04-01 12:57:52 +08:00
6b9f9a107e fix: 手术医嘱类型显示修复 - SQL返回category_enum作为advice_type,前端添加手术类型选项 2026-04-01 12:45:15 +08:00
11a7f49162 fix: 手术医嘱therapy_enum默认为2(临时医嘱),避免被前端过滤 2026-04-01 12:16:36 +08:00
b4e5061b73 Merge remote-tracking branch 'origin/develop' into develop 2026-04-01 11:56:27 +08:00
f5a1ad7f3f fix: 手术医嘱advice_name从content_json解析surgeryName 2026-04-01 11:36:50 +08:00
eeac88b1d1 fix: Bug #318 历史数据修复脚本(Python+SQL) 2026-04-01 10:48:44 +08:00
9f619ccdd4 fix: Bug #318 历史数据修复SQL脚本 2026-04-01 10:28:45 +08:00
22 changed files with 1322 additions and 172 deletions

View File

@@ -570,13 +570,15 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 耗材前端adviceType=4后端ItemType.DEVICE=2
List<AdviceSaveDto> deviceList = adviceSaveList.stream()
.filter(e -> ItemType.DEVICE.getValue().equals(e.getAdviceType())
|| e.getAdviceType() == 4) // 🔧 BugFix: 前端耗材类型值为4
|| e.getAdviceType() == 4) // 前端耗材类型值为4
.collect(Collectors.toList());
// 诊疗活动包括普通诊疗前端adviceType=3会诊前端adviceType=5
// 诊疗活动前端adviceType=3诊疗、adviceType=5会诊、adviceType=6手术
List<AdviceSaveDto> activityList = adviceSaveList.stream()
.filter(e -> ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|| e.getAdviceType() == 3 // 🔧 BugFix: 前端诊疗类型值为3
|| e.getAdviceType() == 5) // 🔧 BugFix: 前端会诊类型值为5
|| e.getAdviceType() == 3 // 前端诊疗类型值为3
|| e.getAdviceType() == 5 // 前端会诊类型值为5
|| ItemType.SURGERY.getValue().equals(e.getAdviceType())) // 🔧 BugFix#318: 手术类型值为6
.collect(Collectors.toList());
// 🔍 Debug日志: 记录分类结果
@@ -605,11 +607,12 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
iDeviceDispenseService.deleteDeviceDispense(adviceSaveDto.getRequestId());
}
// 🔧 Bug Fix: 跳过耗材的库存校验(耗材的库存校验逻辑不同)
// 🔧 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.DEVICE.getValue().equals(e.getAdviceType())
&& !ItemType.SURGERY.getValue().equals(e.getAdviceType())) // 🔧 BugFix#318: 排除手术类型
.collect(Collectors.toList());
// 校验库存
String tipRes = adviceUtils.checkInventory(needCheckList);

View File

@@ -161,6 +161,11 @@ public class RequestBaseDto {
private String doseUnitCode;
private String doseUnitCode_dictText;
/**
* 单价
*/
private BigDecimal unitPrice;
/**
* 总价
*/
@@ -215,4 +220,16 @@ public class RequestBaseDto {
@JsonSerialize(using = ToStringSerializer.class)
private Long basedOnId;
/**
* 就诊id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long encounterId;
/**
* 患者id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long patientId;
}

View File

@@ -292,7 +292,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
surgeryChargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(surgeryServiceRequest.getBusNo()));
surgeryChargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue());
surgeryChargeItem.setPatientId(patientId);
surgeryChargeItem.setContextEnum(3); // 3-诊疗
surgeryChargeItem.setContextEnum(6); // 6-手术
surgeryChargeItem.setEncounterId(encounterId);
surgeryChargeItem.setEntererId(practitionerId);
surgeryChargeItem.setEnteredDate(curDate);

View File

@@ -471,6 +471,7 @@
T1.dose AS dose,
T1.dose_unit_code AS dose_unit_code,
T4.id AS charge_item_id,
T4.unit_price AS unit_price,
T4.total_price AS total_price,
T4.status_enum AS charge_status,
al.id AS position_id,
@@ -480,7 +481,9 @@
ccd.name AS condition_definition_name,
T1.sort_number AS sort_number,
T1.based_on_id AS based_on_id,
T1.category_enum AS category_enum
T1.category_enum AS category_enum,
T1.encounter_id AS encounter_id,
T1.patient_id AS patient_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'
@@ -523,6 +526,7 @@
NULL AS dose,
'' AS dose_unit_code,
T3.id AS charge_item_id,
T3.unit_price AS unit_price,
T3.total_price AS total_price,
T3.status_enum AS charge_status,
al.id AS position_id,
@@ -532,7 +536,9 @@
'' AS condition_definition_name,
99 AS sort_number,
T1.based_on_id AS based_on_id,
T1.category_enum AS category_enum
T1.category_enum AS category_enum,
T1.encounter_id AS encounter_id,
T1.patient_id AS patient_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'
@@ -550,7 +556,7 @@
AND T1.refund_device_id IS NULL
ORDER BY T1.status_enum)
UNION ALL
(SELECT 3 AS advice_type,
(SELECT CASE WHEN T1.category_enum = 4 THEN 6 ELSE COALESCE(T1.category_enum, 3) END AS advice_type,
T1.id AS request_id,
T1.id || '-3' AS unique_key,
'' AS prescription_no,
@@ -561,18 +567,19 @@
null AS skin_test_flag,
null AS inject_flag,
null AS group_id,
COALESCE(T2.NAME, CASE WHEN T1.content_json IS NOT NULL AND T1.content_json != '' THEN T1.content_json::json->>'adviceName' ELSE NULL END) AS advice_name,
COALESCE(T2.NAME, T1.content_json::jsonb->>'surgeryName', T1.content_json::jsonb->>'adviceName') AS advice_name,
'' AS volume,
'' AS lot_number,
T1.quantity AS quantity,
T1.unit_code AS unit_code,
T1.status_enum AS status_enum,
'' AS method_code,
'' AS rate_code,
NULL AS dose,
'' AS dose_unit_code,
T3.id AS charge_item_id,
T3.total_price AS total_price,
T1.quantity AS quantity,
T1.unit_code AS unit_code,
T1.status_enum AS status_enum,
'' AS method_code,
'' AS rate_code,
NULL AS dose,
'' AS dose_unit_code,
T3.id AS charge_item_id,
T3.unit_price AS unit_price,
T3.total_price AS total_price,
T3.status_enum AS charge_status,
ao.id AS position_id,
ao.name AS position_name,
@@ -580,9 +587,11 @@
1 AS part_percent,
'' AS condition_definition_name,
99 AS sort_number,
T1.based_on_id AS based_on_id,
T1.category_enum AS category_enum
FROM wor_service_request AS T1
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
FROM wor_service_request AS T1
LEFT JOIN wor_activity_definition AS T2
ON T2.ID = T1.activity_id
AND T2.delete_flag = '0'

View File

@@ -203,6 +203,7 @@
T1.dose AS dose,
T1.dose_unit_code AS dose_unit_code,
T4.id AS charge_item_id,
T4.unit_price AS unit_price,
T4.total_price AS total_price,
T4.status_enum AS charge_status,
al.id AS position_id,
@@ -254,6 +255,7 @@
NULL AS dose,
'' AS dose_unit_code,
T3.id AS charge_item_id,
T3.unit_price AS unit_price,
T3.total_price AS total_price,
T3.status_enum AS charge_status,
al.id AS position_id,
@@ -281,7 +283,7 @@
AND T1.refund_device_id IS NULL
ORDER BY T1.status_enum)
UNION ALL
(SELECT 3 AS advice_type,
(SELECT CASE WHEN T1.category_enum = 4 THEN 6 ELSE COALESCE(T1.category_enum, 3) END AS advice_type,
T1.id AS request_id,
T1.id || '-3' AS unique_key,
T1.requester_id AS requester_id,
@@ -291,7 +293,7 @@
null AS skin_test_flag,
null AS inject_flag,
null AS group_id,
T2.NAME AS advice_name,
COALESCE(T2.NAME, T1.content_json::jsonb->>'surgeryName') AS advice_name,
'' AS volume,
'' AS lot_number,
T1.quantity AS quantity,
@@ -302,6 +304,7 @@
NULL AS dose,
'' AS dose_unit_code,
T3.id AS charge_item_id,
T3.unit_price AS unit_price,
T3.total_price AS total_price,
T3.status_enum AS charge_status,
ao.id AS position_id,
@@ -309,7 +312,7 @@
null AS dispense_per_duration,
1 AS part_percent,
'' AS condition_definition_name,
T1.therapy_enum AS therapyEnum,
COALESCE(T1.therapy_enum, 2) AS therapyEnum,
99 AS sort_number,
T1.based_on_id AS based_on_id
FROM wor_service_request AS T1

View File

@@ -26,12 +26,13 @@
<select id="getRequestFormDetail" resultType="com.openhis.web.regdoctorstation.dto.RequestFormDetailQueryDto">
SELECT wsr.quantity,
wsr.unit_code,
wad.NAME AS advice_name,
COALESCE(wad.NAME, wsr.content_json::jsonb->>'surgeryName') AS advice_name,
aci.total_price
FROM wor_service_request AS wsr
LEFT JOIN wor_activity_definition AS wad ON wad.ID = wsr.activity_id
AND wad.delete_flag = '0'
LEFT JOIN adm_charge_item AS aci ON aci.service_id = wsr.ID
AND aci.service_table = 'wor_service_request'
AND aci.delete_flag = '0'
WHERE wsr.delete_flag = '0'
AND wsr.prescription_no = #{prescriptionNo}

View File

@@ -23,14 +23,19 @@ public enum ItemType implements HisEnumInterface {
MEDICINE(1, "1", "药品"),
/**
* 耗材
* 医疗活动
*/
ACTIVITY(3, "3", "医疗活动"),
/**
* 耗材前端使用值2
*/
DEVICE(2, "2", "耗材"),
/**
* 医疗活动
* 手术前端使用值6避免与耗材冲突
*/
ACTIVITY(3, "3", "医疗活动");
SURGERY(6, "6", "手术");
@EnumValue
private Integer value;

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env node
/**
* Fix lodash.template assignWith issue - Manual fix script
* Run this after npm/yarn install and before build
@@ -18,34 +17,35 @@ if (!fs.existsSync(lodashTemplatePath)) {
let content = fs.readFileSync(lodashTemplatePath, 'utf8');
// Check if already patched
if (content.includes('/* LODASH_TEMPLATE_PATCHED */')) {
console.log('✓ lodash.template already patched');
// Check if already patched with new version
if (content.includes('/* LODASH_TEMPLATE_PATCHED_V2 */')) {
console.log('✓ lodash.template already patched with v2');
process.exit(0);
}
// Check if assignWith is actually missing
if (content.includes('assignWith')) {
// Check if it's used but not defined
const hasFunctionDefinition = /function\s+assignWith|var\s+assignWith\s*=/.test(content);
if (hasFunctionDefinition) {
console.log('✓ assignWith is already defined in lodash.template');
process.exit(0);
}
// Remove old patch if exists
if (content.includes('/* LODASH_TEMPLATE_PATCHED */')) {
console.log('🔄 Removing old patch...');
content = content.replace(/\/\* LODASH_TEMPLATE_PATCHED \*\/\n.*assignWith[\s\S]*?^\}/m, '');
}
console.log('🔧 Patching lodash.template...');
console.log('🔧 Patching lodash.template with v2...');
// Simple assignWith implementation
const assignWithImpl = `/* LODASH_TEMPLATE_PATCHED */
// assignWith polyfill for lodash.template
// Correct assignWith implementation that handles undefined customizer
const assignWithImpl = `/* LODASH_TEMPLATE_PATCHED_V2 */
// assignWith polyfill for lodash.template - Fixed version
function assignWith(object, source, customizer) {
if (object == null) return object;
if (typeof customizer !== 'function') {
customizer = function(objValue, srcValue) {
return srcValue;
};
}
var props = Object.keys(Object(source));
for (var i = 0; i < props.length; i++) {
var key = props[i];
var value = source[key];
var assignedValue = customizer ? customizer(object[key], value, key, object, source) : value;
var assignedValue = customizer(object[key], value, key, object, source);
if (assignedValue !== undefined) {
object[key] = assignedValue;
}
@@ -74,5 +74,5 @@ const after = content.substring(insertPos);
content = before + '\n' + assignWithImpl + after;
fs.writeFileSync(lodashTemplatePath, content);
console.log('✅ Successfully patched lodash.template with assignWith function');
console.log('✅ Successfully patched lodash.template with assignWith function (v2)');
console.log(' File:', lodashTemplatePath);

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# Fix lodash.template assignWith issue for Linux/Mac servers
# Fix lodash.template assignWith issue for Linux/Mac servers - V2
# Run this script after npm/yarn install
LODGASH_TEMPLATE="node_modules/lodash.template/index.js"
@@ -9,34 +9,43 @@ if [ ! -f "$LODGASH_TEMPLATE" ]; then
exit 1
fi
# Check if already patched
if grep -q "LODASH_TEMPLATE_PATCHED" "$LODGASH_TEMPLATE"; then
echo "✓ lodash.template already patched"
# Check if already patched with v2
if grep -q "LODASH_TEMPLATE_PATCHED_V2" "$LODGASH_TEMPLATE"; then
echo "✓ lodash.template already patched with v2"
exit 0
fi
echo "🔧 Patching lodash.template..."
# Remove old patch if exists
if grep -q "LODASH_TEMPLATE_PATCHED" "$LODGASH_TEMPLATE"; then
echo "🔄 Removing old patch..."
# Use sed to remove old patch (multi-line)
sed -i '/\/\* LODASH_TEMPLATE_PATCHED \*\//,/^}$/d' "$LODGASH_TEMPLATE"
fi
echo "🔧 Patching lodash.template with v2..."
# Create a temporary file with the patch
PATCH=$(cat <<'EOF'
/* LODASH_TEMPLATE_PATCHED */
// assignWith polyfill for lodash.template
PATCH='/* LODASH_TEMPLATE_PATCHED_V2 */
// assignWith polyfill for lodash.template - Fixed version
function assignWith(object, source, customizer) {
if (object == null) return object;
if (typeof customizer !== "function") {
customizer = function(objValue, srcValue) {
return srcValue;
};
}
var props = Object.keys(Object(source));
for (var i = 0; i < props.length; i++) {
var key = props[i];
var value = source[key];
var assignedValue = customizer ? customizer(object[key], value, key, object, source) : value;
var assignedValue = customizer(object[key], value, key, object, source);
if (assignedValue !== undefined) {
object[key] = assignedValue;
}
}
return object;
}
EOF
)
'
# Insert after the first line (license comment)
{
@@ -45,4 +54,4 @@ EOF
tail -n +2 "$LODGASH_TEMPLATE"
} > "$LODGASH_TEMPLATE.tmp" && mv "$LODGASH_TEMPLATE.tmp" "$LODGASH_TEMPLATE"
echo "✅ Successfully patched lodash.template with assignWith function"
echo "✅ Successfully patched lodash.template with assignWith function (v2)"

View File

@@ -9,7 +9,10 @@
"predev": "node utils/check.js dev && vdoing",
"prebuild": "node utils/check.js build && vdoing",
"updateTheme": "yarn remove vuepress-theme-vdoing && rm -rf node_modules && yarn && yarn add vuepress-theme-vdoing -D",
"editFm": "node utils/editFrontmatter.js"
"editFm": "node utils/editFrontmatter.js",
"fix-lodash": "node fix-lodash-manual.js",
"build:fixed": "npm run fix-lodash && npm run build",
"build:win:fixed": "npm run fix-lodash && npm run build:win"
},
"license": "MIT",
"engines": {
@@ -23,6 +26,7 @@
"inquirer": "^7.1.0",
"json2yaml": "^1.1.0",
"lodash": "^4.17.21",
"lodash.assignwith": "^4.2.0",
"vuepress": "1.9.9",
"vuepress-plugin-baidu-tongji": "^1.0.1",
"vuepress-plugin-demo-block": "^0.7.2",
@@ -33,5 +37,13 @@
"vuepress-theme-vdoing": "^1.12.9",
"yamljs": "^0.3.0"
},
"dependencies": {}
"dependencies": {},
"resolutions": {
"lodash": "^4.17.21",
"lodash.template": "^4.5.0"
},
"overrides": {
"lodash": "^4.17.21",
"lodash.template": "^4.5.0"
}
}

View File

@@ -571,7 +571,7 @@
<script setup>
import {getOrgTree, saveOrderGroup} from './api';
import adviceBaseList from './adviceBaseList';
import {getCurrentInstance, nextTick, watch} from 'vue';
import {computed, getCurrentInstance, nextTick, ref, watch} from 'vue';
import {calculateQuantityByDays, formatNumber} from '@/utils/his';
const emit = defineEmits(['selectDiagnosis']);
@@ -620,31 +620,31 @@ const stockList = ref([]);
const { proxy } = getCurrentInstance();
const inputRefs = ref({}); // 存储输入框实例
const requiredProps = ref([]); // 存储必填项 prop 顺序
const { method_code, unit_code, rate_code, distribution_category_code } = proxy.useDict(
const { method_code, unit_code, rate_code, distribution_category_code, drord_doctor_type } = proxy.useDict(
'method_code',
'unit_code',
'rate_code',
'distribution_category_code'
'distribution_category_code',
'drord_doctor_type'
);
const adviceTypeList = ref([
{
label: '西药中成药',
value: 1,
},
{
label: '耗材',
value: 2,
},
{
label: '诊疗',
value: 3,
},
{
label: '全部',
value: undefined,
},
]);
// 使用 drord_doctor_type 字典
const adviceTypeList = computed(() => {
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
const list = drord_doctor_type.value.map(item => ({
label: item.label,
value: parseInt(item.value) || item.value
}));
return [...list, { label: '全部', value: undefined }];
}
// 默认值
return [
{ label: '西药中成药', value: 1 },
{ label: '耗材', value: 4 },
{ label: '诊疗', value: 3 },
{ label: '全部', value: undefined },
];
});
onMounted(() => {
document.addEventListener('keydown', escKeyListener);
});

View File

@@ -952,34 +952,26 @@ const { method_code, unit_code, rate_code, distribution_category_code, drord_doc
'drord_doctor_type'
);
// 删除硬编码的adviceTypeList直接使用drord_doctor_type字典
// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=会诊, 6=全部
const adviceTypeList = ref([
{
label: '西药',
value: 1,
},
{
label: '中成药',
value: 2,
},
{
label: '诊疗',
value: 3,
},
{
label: '耗材',
value: 4,
},
{
label: '会诊',
value: 5,
},
{
label: '全部',
value: '',
},
]);
// 使用 drord_doctor_type 字典,不再硬编码
// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=会诊, 6=手术
const adviceTypeList = computed(() => {
// 如果字典已加载,使用字典数据;否则使用默认值
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
return drord_doctor_type.value.map(item => ({
label: item.label,
value: parseInt(item.value) || item.value
}));
}
// 默认返回值,确保页面正常显示
return [
{ label: '西药', value: 1 },
{ label: '中成药', value: 2 },
{ label: '诊疗', value: 3 },
{ label: '耗材', value: 4 },
{ label: '会诊', value: 5 },
{ label: '手术', value: 6 },
];
});
// 根据类型值获取显示标签,避免非编辑态出现空标签
const mapAdviceTypeLabel = (type) => {
@@ -2386,6 +2378,8 @@ function handleSave(prescriptionId) {
saveAdviceType = 1; // 中成药前端2 -> 后端1
} else if (item.adviceType == 5) {
saveAdviceType = 3; // 会诊前端5 -> 后端3诊疗类
} else if (item.adviceType == 6) {
saveAdviceType = 6; // 🔧 BugFix#318: 手术类型保持为6
}
// 🔧 Bug Fix: Validate and fix NaN values before sending to backend
@@ -2395,6 +2389,21 @@ function handleSave(prescriptionId) {
console.warn('Fixed NaN totalPrice for item:', item.adviceName);
}
// 🔧 BugFix#318: 从 parsedContent 提取标准医嘱字段,排除手术特有字段
const standardFields = [
'accountId', 'chargeItemId', 'conditionDefinitionId', 'conditionId',
'contentJson', 'definitionDetailId', 'definitionId', 'diagnosisName',
'dosageInstruction', 'effectiveOrgId', 'encounterDiagnosisId',
'encounterId', 'lotNumber', 'patientId', 'practitionerId',
'prescriptionNo', 'skinTestFlag', 'unitPrice', 'volume', 'ybClassEnum'
];
let filteredContent = {};
standardFields.forEach(field => {
if (parsedContent[field] !== undefined) {
filteredContent[field] = parsedContent[field];
}
});
// 构造请求参数
// 🔧 Bug Fix: 确保库存匹配成功的关键字段
// 耗材使用 adm_device_definition 表
@@ -2418,10 +2427,12 @@ function handleSave(prescriptionId) {
});
return {
...parsedContent,
...filteredContent, // 🔧 BugFix#318: 使用过滤后的字段,排除手术特有字段
adviceType: saveAdviceType, // 使用转换后的类型
requestId: item.requestId,
dbOpType: '1',
dbOpType: item.requestId ? '2' : '1', // 🔧 BugFix: 根据requestId判断是新增还是修改
encounterId: item.encounterId || props.patientInfo.encounterId, // 🔧 BugFix: 确保encounterId
patientId: item.patientId || props.patientInfo.patientId, // 🔧 BugFix: 确保patientId
groupId: item.groupId,
uniqueKey: undefined,
// 使用转换后的数量和单位
@@ -2728,28 +2739,32 @@ function handleSaveSign(row, index, prescriptionId) {
formRef.validate((valid) => {
if (valid) {
if (row.adviceType != 2) {
// 🔧 BugFix#318: 手术类型(adviceType=6)不需要检查绑定耗材/药品
if (row.adviceType != 2 && row.adviceType != 6) {
// 1:用法绑东西 2:诊疗绑东西
let typeCode = row.adviceType == 1 ? '1' : '2';
// 用法字典值/诊疗定义id
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
getBindDevice({ typeCode: typeCode, itemNo: itemNo }).then((res) => {
if (res.data.length == 0) {
return;
}
// 是否需要打开弹窗
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
if (!JSON.parse(openBindDialog)) {
proxy.$refs['orderBindInfoRef'].open(res.data);
} else {
// 如果弹窗不提示带出的项目,自动带出
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
if (!bindMethod.value[itemNo]) {
handleOrderBindInfo(res.data, row.methodCode);
bindMethod.value[itemNo] = true;
// 🔧 确保 itemNo 有值才调用接口
if (itemNo) {
getBindDevice({ typeCode: typeCode, itemNo: itemNo }).then((res) => {
if (res.data.length == 0) {
return;
}
}
});
// 是否需要打开弹窗
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
if (!JSON.parse(openBindDialog)) {
proxy.$refs['orderBindInfoRef'].open(res.data);
} else {
// 如果弹窗不提示带出的项目,自动带出
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
if (!bindMethod.value[itemNo]) {
handleOrderBindInfo(res.data, row.methodCode);
bindMethod.value[itemNo] = true;
}
}
});
}
}
row.isEdit = false;
isAdding.value = false;
@@ -2771,15 +2786,20 @@ function handleSaveSign(row, index, prescriptionId) {
row.conditionId = conditionId.value;
// 处理总量为小单位情况,需要把单价也保存成小单位的
if (row.unitCodeList.find((item) => item.value == row.unitCode).type == 'unit') {
if (row.adviceType != 3) {
row.unitPrice = row.unitTempPrice;
// 🔧 BugFix#318: 手术类型(adviceType=6)可能没有unitCodeList需要判断
if (row.unitCodeList && row.unitCodeList.length > 0) {
const foundUnit = row.unitCodeList.find((item) => item.value == row.unitCode);
if (foundUnit && foundUnit.type == 'unit') {
if (row.adviceType != 3) {
row.unitPrice = row.unitTempPrice;
}
} else {
const minUnitItem = row.unitCodeList.find((item) => item.value == row.minUnitCode);
if (minUnitItem) {
row.unitCode_dictText = minUnitItem.label;
}
row.unitPrice = row.minUnitPrice;
}
} else {
row.unitCode_dictText = row.unitCodeList.find(
(item) => item.value == row.minUnitCode
).label;
row.unitPrice = row.minUnitPrice;
}
row.conditionDefinitionId = conditionDefinitionId.value;
row.encounterDiagnosisId = encounterDiagnosisId.value;
@@ -2962,8 +2982,27 @@ function handleSaveBatch(prescriptionId) {
saveAdviceType = 1; // 中成药前端2 -> 后端1
} else if (item.adviceType == 5) {
saveAdviceType = 3; // 会诊前端5 -> 后端3诊疗类
} else if (item.adviceType == 6) {
saveAdviceType = 6; // 🔧 BugFix#318: 手术类型保持为6
}
// 🔧 BugFix#318: 过滤掉手术特有字段,只保留标准医嘱字段
const standardItemFields = [
'adviceDefinitionId', 'adviceName', 'adviceTableName', 'adviceType',
'basedOnId', 'chargeItemId', 'chargeStatus', 'conditionDefinitionId',
'conditionId', 'contentJson', 'dose', 'doseUnitCode', 'encounterDiagnosisId',
'encounterId', 'groupId', 'injectFlag', 'lotNumber', 'methodCode', 'partPercent',
'patientId', 'positionId', 'positionName', 'prescriptionNo', 'quantity', 'rateCode',
'requestId', 'skinTestFlag', 'sortNumber', 'statusEnum', 'totalPrice',
'unitCode', 'unitPrice', 'volume', 'ybClassEnum'
];
let filteredItem = {};
standardItemFields.forEach(field => {
if (item[field] !== undefined) {
filteredItem[field] = item[field];
}
});
// 构造 contentJson (保持前端UI原始数据)
const itemToSave = {
...item,
@@ -3011,7 +3050,7 @@ function handleSaveBatch(prescriptionId) {
});
return {
...item,
...filteredItem, // 🔧 BugFix#318: 使用过滤后的字段
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
adviceType: saveAdviceType,

View File

@@ -25,6 +25,15 @@
>
<el-table-column type="index" label="序号" width="60" align="center" />
<!-- 手术单号 -->
<el-table-column label="手术单号" align="center" width="150">
<template #default="scope">
<el-link type="primary" @click="handleView(scope.row)">
{{ scope.row.surgeryNo || '-' }}
</el-link>
</template>
</el-table-column>
<!-- 申请日期 -->
<el-table-column label="申请日期" align="center" prop="createTime" width="180">
<template #default="scope">
@@ -32,9 +41,6 @@
</template>
</el-table-column>
<!-- 手术单号 -->
<el-table-column label="手术单号" align="center" prop="surgeryNo" width="150" show-overflow-tooltip />
<!-- 患者姓名 -->
<el-table-column label="患者姓名" align="center" prop="patientName" width="100" />

View File

@@ -26,10 +26,16 @@
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="手术单号" width="160" align="center">
<template #default="scope">
<el-link type="primary" @click="handleViewDetail(scope.row)">
{{ scope.row.prescriptionNo || '-' }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="patientName" label="患者姓名" width="120" />
<el-table-column prop="name" label="申请单名称" width="140" />
<el-table-column prop="createTime" label="创建时间" width="160" />
<el-table-column prop="prescriptionNo" label="处方号" width="140" />
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">

View File

@@ -371,7 +371,7 @@
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span style="font-size: 16px; font-weight: 600">
{{ row.adviceName }}
{{ row.unitPrice ? Number(row.unitPrice).toFixed(2) + '/次' : '-' + '元' }}
{{ row.unitPrice ? ' -' + Number(row.unitPrice).toFixed(2) + '' : ' -元' }}
</span>
<div class="form-group">
<el-form-item

View File

@@ -426,34 +426,35 @@ const inputRefs = ref({}); // 存储输入框实例
const requiredProps = ref([]); // 存储必填项 prop 顺序
const totalAmount = ref(0);
const therapyEnum = ref('');
const { method_code, unit_code, rate_code, distribution_category_code } = proxy.useDict(
const { method_code, unit_code, rate_code, distribution_category_code, drord_doctor_type } = proxy.useDict(
'method_code',
'unit_code',
'rate_code',
'distribution_category_code'
'distribution_category_code',
'drord_doctor_type'
);
const openDrawer = ref(false);
const orderClassCode = ref('');
const orderStatus = ref('');
// 医嘱类型
const adviceTypeList = ref([
{
label: '西药中成药',
value: 1,
},
// {
// label: '耗材',
// value: 2,
// },
{
label: '诊疗',
value: 3,
},
{
label: '全部',
value: '',
},
]);
// 医嘱类型 - 使用 drord_doctor_type 字典
const adviceTypeList = computed(() => {
// 如果字典已加载,使用字典数据(过滤掉全部选项)
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
const list = drord_doctor_type.value.map(item => ({
label: item.label,
value: parseInt(item.value) || item.value
}));
// 添加全部选项
return [...list, { label: '全部', value: '' }];
}
// 默认返回值
return [
{ label: '西药中成药', value: 1 },
{ label: '诊疗', value: 3 },
{ label: '手术', value: 6 },
{ label: '全部', value: '' },
];
});
// 医嘱状态
const statusOption = [
{

View File

@@ -250,12 +250,15 @@
</template>
<script setup>
import {computed, onMounted, reactive, ref, watch} from 'vue';
import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from 'vue';
import {ElMessage} from 'element-plus';
import {formatDateStr} from '@/utils/index';
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
import useUserStore from '@/store/modules/user';
const { proxy } = getCurrentInstance();
const { drord_doctor_type } = proxy.useDict('drord_doctor_type');
// Props定义
const props = defineProps({
visible: {
@@ -280,11 +283,26 @@ const dialogVisible = computed({
get: () => props.visible,
set: (value) => emit('update:visible', value),
});
const adviceTypeList = ref([
{ label: '耗材', value: 2 },
{ label: '诊疗', value: 3 },
{ label: '全部', value: '' },
]);
// 使用 drord_doctor_type 字典
const adviceTypeList = computed(() => {
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
// 只保留耗材(4)和诊疗(3)类型,并添加全部选项
const filtered = drord_doctor_type.value.filter(item => {
const val = parseInt(item.value);
return val === 3 || val === 4;
}).map(item => ({
label: item.label,
value: parseInt(item.value)
}));
return [...filtered, { label: '全部', value: '' }];
}
// 默认值
return [
{ label: '耗材', value: 4 },
{ label: '诊疗', value: 3 },
{ label: '全部', value: '' },
];
});
const adviceType = ref('');
const feeItemsList = ref([]);
const executeTime = ref('');

351
scripts/repair_bug_318.py Normal file
View File

@@ -0,0 +1,351 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Bug #318 历史数据修复脚本
为已存在但未生成手术医嘱的手术申请单补齐数据
"""
import psycopg2
import json
import random
import string
from datetime import datetime
# 数据库连接配置
DB_CONFIG = {
"host": "47.116.196.11",
"port": 15432,
"database": "postgresql",
"user": "postgresql",
"password": "postgresql", # 请根据实际情况修改
}
def generate_bus_no():
"""生成4位随机bus_no"""
return "".join(random.choices(string.digits, k=4))
def check_repair_needed(conn):
"""检查需要修复的记录数"""
cursor = conn.cursor()
sql = """
SELECT COUNT(*)
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL
"""
cursor.execute(sql)
count = cursor.fetchone()[0]
cursor.close()
return count
def get_repair_list(conn):
"""获取需要修复的手术申请单列表"""
cursor = conn.cursor()
sql = """
SELECT
rf.id,
rf.prescription_no,
rf.encounter_id,
rf.patient_id,
rf.requester_id,
rf.create_time,
rf.org_id,
rf.desc_json
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL
ORDER BY rf.create_time DESC
"""
cursor.execute(sql)
results = cursor.fetchall()
cursor.close()
return results
def parse_surgery_info(desc_json):
"""解析手术信息"""
if not desc_json:
return {}
try:
if isinstance(desc_json, str):
return json.loads(desc_json)
return desc_json
except:
return {}
def repair_service_request(conn, repair_list):
"""修复手术医嘱ServiceRequest"""
cursor = conn.cursor()
insert_sql = """
INSERT INTO wor_service_request (
bus_no, prescription_no, status_enum, generate_source_enum,
therapy_enum, quantity, unit_code, category_enum,
patient_id, requester_id, encounter_id, authored_time,
org_id, content_json, delete_flag, create_time, create_by, tenant_id
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING id
"""
new_requests = []
for record in repair_list:
(
rf_id,
prescription_no,
encounter_id,
patient_id,
requester_id,
create_time,
org_id,
desc_json,
) = record
# 解析手术信息
surgery_info = parse_surgery_info(desc_json)
content_json = (
json.dumps(surgery_info, ensure_ascii=False) if surgery_info else None
)
# 生成bus_no
bus_no = generate_bus_no()
# 插入ServiceRequest
cursor.execute(
insert_sql,
(
bus_no, # bus_no
prescription_no, # prescription_no
1, # status_enum: 1-待签发
1, # generate_source_enum: 1-医生处方
2, # therapy_enum: 2-临时医嘱
1, # quantity
"", # unit_code
4, # category_enum: 4-手术
patient_id, # patient_id
requester_id, # requester_id
encounter_id, # encounter_id
create_time, # authored_time
org_id, # org_id
content_json, # content_json
"0", # delete_flag
create_time, # create_time
requester_id, # create_by
1, # tenant_id
),
)
service_request_id = cursor.fetchone()[0]
new_requests.append(
{
"service_request_id": service_request_id,
"bus_no": bus_no,
"prescription_no": prescription_no,
"patient_id": patient_id,
"encounter_id": encounter_id,
"requester_id": requester_id,
"create_time": create_time,
"org_id": org_id,
"surgery_info": surgery_info,
}
)
conn.commit()
cursor.close()
return new_requests
def repair_charge_items(conn, new_requests):
"""修复收费项目ChargeItem"""
cursor = conn.cursor()
insert_sql = """
INSERT INTO adm_charge_item (
bus_no, status_enum, generate_source_enum, patient_id,
context_enum, encounter_id, enterer_id, entered_date,
service_table, service_id, product_table, requesting_org_id,
quantity_value, quantity_unit, unit_price, total_price,
delete_flag, create_time, create_by, tenant_id
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
for req in new_requests:
surgery_info = req["surgery_info"]
# 手术费用
surgery_fee = 0
if surgery_info and "surgeryFee" in surgery_info:
try:
surgery_fee = float(surgery_info["surgeryFee"])
except:
surgery_fee = 0
# 插入手术费用收费项目
cursor.execute(
insert_sql,
(
"CI" + req["bus_no"], # bus_no
1, # status_enum: 1-草稿
1, # generate_source_enum: 1-医生处方
req["patient_id"], # patient_id
3, # context_enum: 3-诊疗
req["encounter_id"], # encounter_id
req["requester_id"], # enterer_id
req["create_time"], # entered_date
"wor_service_request", # service_table
req["service_request_id"], # service_id
"wor_activity_definition", # product_table
req["org_id"], # requesting_org_id
1, # quantity_value
"", # quantity_unit
surgery_fee, # unit_price
surgery_fee, # total_price
"0", # delete_flag
req["create_time"], # create_time
req["requester_id"], # create_by
1, # tenant_id
),
)
# 麻醉费用如果存在且大于0
anesthesia_fee = 0
if surgery_info and "anesthesiaFee" in surgery_info:
try:
anesthesia_fee = float(surgery_info["anesthesiaFee"])
except:
anesthesia_fee = 0
if anesthesia_fee > 0:
cursor.execute(
insert_sql,
(
"CI" + req["bus_no"] + "_A", # bus_no
1, # status_enum
1, # generate_source_enum
req["patient_id"], # patient_id
3, # context_enum
req["encounter_id"], # encounter_id
req["requester_id"], # enterer_id
req["create_time"], # entered_date
"wor_service_request", # service_table
req["service_request_id"], # service_id
"wor_activity_definition", # product_table
req["org_id"], # requesting_org_id
1, # quantity_value
"", # quantity_unit
anesthesia_fee, # unit_price
anesthesia_fee, # total_price
"0", # delete_flag
req["create_time"], # create_time
req["requester_id"], # create_by
1, # tenant_id
),
)
conn.commit()
cursor.close()
def verify_repair(conn):
"""验证修复结果"""
cursor = conn.cursor()
# 统计手术医嘱数量
cursor.execute("""
SELECT COUNT(*)
FROM wor_service_request
WHERE category_enum = 4 AND delete_flag = '0'
""")
service_request_count = cursor.fetchone()[0]
# 统计收费项目数量
cursor.execute("""
SELECT COUNT(*)
FROM adm_charge_item ci
WHERE ci.service_table = 'wor_service_request'
AND EXISTS (
SELECT 1 FROM wor_service_request sr
WHERE sr.id = ci.service_id AND sr.category_enum = 4
)
""")
charge_item_count = cursor.fetchone()[0]
cursor.close()
return service_request_count, charge_item_count
def main():
"""主函数"""
print("=" * 60)
print("Bug #318 历史数据修复脚本")
print("=" * 60)
print()
try:
# 连接数据库
print("正在连接数据库...")
conn = psycopg2.connect(**DB_CONFIG)
print("✓ 数据库连接成功")
print()
# 步骤1检查需要修复的记录数
print("步骤1: 检查需要修复的记录...")
need_repair_count = check_repair_needed(conn)
print(f"✓ 发现 {need_repair_count} 条需要修复的手术申请单")
print()
if need_repair_count == 0:
print("没有需要修复的数据,退出")
return
# 步骤2获取修复列表
print("步骤2: 获取修复列表...")
repair_list = get_repair_list(conn)
print(f"✓ 获取到 {len(repair_list)} 条记录")
print()
# 步骤3修复ServiceRequest
print("步骤3: 生成手术医嘱ServiceRequest...")
new_requests = repair_service_request(conn, repair_list)
print(f"✓ 成功生成 {len(new_requests)} 条手术医嘱")
print()
# 步骤4修复ChargeItem
print("步骤4: 生成收费项目ChargeItem...")
repair_charge_items(conn, new_requests)
print(f"✓ 成功生成收费项目")
print()
# 步骤5验证修复结果
print("步骤5: 验证修复结果...")
service_count, charge_count = verify_repair(conn)
print(f"✓ 当前手术医嘱总数: {service_count}")
print(f"✓ 当前手术收费项目总数: {charge_count}")
print()
print("=" * 60)
print("✓ 修复完成!")
print("=" * 60)
except Exception as e:
print(f"✗ 错误: {e}")
import traceback
traceback.print_exc()
finally:
if "conn" in locals():
conn.close()
print("\n数据库连接已关闭")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,240 @@
-- ============================================================
-- Bug #318 历史数据修复 - 简化版SQL脚本
-- 为已存在但未生成手术医嘱的手术申请单补齐数据
-- ============================================================
-- 执行说明:
-- 1. 在数据库管理工具中执行(如 DBeaver、pgAdmin
-- 2. 建议先执行查询部分确认数据
-- 3. 备份数据库后再执行修复
-- ============================================================
-- 步骤1: 查看需要修复的记录数
-- ============================================================
SELECT
'需要修复的手术申请单数量' as ,
COUNT(*) as
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL;
-- 步骤2: 查看详细列表(可选)
-- ============================================================
/*
SELECT
rf.id as request_form_id,
rf.prescription_no as 手术单号,
rf.encounter_id as 就诊ID,
rf.patient_id as 患者ID,
rf.create_time as 申请时间
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL
ORDER BY rf.create_time DESC
LIMIT 10;
*/
-- 步骤3: 执行修复 - 生成手术医嘱ServiceRequest
-- ============================================================
-- ⚠️ 请在确认备份数据库后执行以下语句
INSERT INTO wor_service_request (
bus_no,
prescription_no,
status_enum,
generate_source_enum,
therapy_enum,
quantity,
unit_code,
category_enum,
patient_id,
requester_id,
encounter_id,
authored_time,
org_id,
content_json,
delete_flag,
create_time,
create_by,
tenant_id
)
SELECT
LPAD(FLOOR(RANDOM() * 10000)::TEXT, 4, '0') as bus_no, -- 生成4位随机数
rf.prescription_no,
1 as status_enum, -- 1-待签发
1 as generate_source_enum, -- 1-医生处方
2 as therapy_enum, -- 2-临时医嘱
1 as quantity,
'' as unit_code,
4 as category_enum, -- 4-手术
rf.patient_id,
rf.requester_id,
rf.encounter_id,
rf.create_time as authored_time,
rf.org_id,
rf.desc_json as content_json, -- 存储手术详情
'0' as delete_flag,
rf.create_time,
rf.requester_id as create_by,
1 as tenant_id
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL;
-- 步骤4: 查看生成的手术医嘱数量
-- ============================================================
SELECT
'生成的手术医嘱数量' as ,
COUNT(*) as
FROM wor_service_request
WHERE category_enum = 4
AND delete_flag = '0'
AND create_time > CURRENT_TIMESTAMP - INTERVAL '1 hour'; -- 最近1小时生成的
-- 步骤5: 生成手术收费项目ChargeItem
-- ============================================================
-- 先创建临时表存储新生成的ServiceRequest
CREATE TEMPORARY TABLE temp_new_service_requests AS
SELECT
sr.id as service_request_id,
sr.bus_no,
sr.prescription_no,
sr.patient_id,
sr.encounter_id,
sr.requester_id,
sr.create_time,
sr.org_id,
sr.content_json
FROM wor_service_request sr
WHERE sr.category_enum = 4
AND sr.delete_flag = '0'
AND sr.create_time > CURRENT_TIMESTAMP - INTERVAL '1 hour'; -- 最近1小时生成的
-- 插入手术费用收费项目
INSERT INTO adm_charge_item (
bus_no,
status_enum,
generate_source_enum,
patient_id,
context_enum,
encounter_id,
enterer_id,
entered_date,
service_table,
service_id,
product_table,
requesting_org_id,
quantity_value,
quantity_unit,
unit_price,
total_price,
delete_flag,
create_time,
create_by,
tenant_id
)
SELECT
'CI' || t.bus_no as bus_no,
1 as status_enum, -- 1-草稿
1 as generate_source_enum, -- 1-医生处方
t.patient_id,
3 as context_enum, -- 3-诊疗
t.encounter_id,
t.requester_id as enterer_id,
t.create_time as entered_date,
'wor_service_request' as service_table,
t.service_request_id as service_id,
'wor_activity_definition' as product_table,
t.org_id as requesting_org_id,
1 as quantity_value,
'' as quantity_unit,
COALESCE((t.content_json::jsonb->>'surgeryFee')::numeric, 0) as unit_price,
COALESCE((t.content_json::jsonb->>'surgeryFee')::numeric, 0) as total_price,
'0' as delete_flag,
t.create_time,
t.requester_id as create_by,
1 as tenant_id
FROM temp_new_service_requests t
WHERE COALESCE((t.content_json::jsonb->>'surgeryFee')::numeric, 0) >= 0;
-- 插入麻醉费用收费项目(如果有麻醉费用)
INSERT INTO adm_charge_item (
bus_no,
status_enum,
generate_source_enum,
patient_id,
context_enum,
encounter_id,
enterer_id,
entered_date,
service_table,
service_id,
product_table,
requesting_org_id,
quantity_value,
quantity_unit,
unit_price,
total_price,
delete_flag,
create_time,
create_by,
tenant_id
)
SELECT
'CI' || t.bus_no || '_A' as bus_no,
1 as status_enum,
1 as generate_source_enum,
t.patient_id,
3 as context_enum,
t.encounter_id,
t.requester_id as enterer_id,
t.create_time as entered_date,
'wor_service_request' as service_table,
t.service_request_id as service_id,
'wor_activity_definition' as product_table,
t.org_id as requesting_org_id,
1 as quantity_value,
'' as quantity_unit,
(t.content_json::jsonb->>'anesthesiaFee')::numeric as unit_price,
(t.content_json::jsonb->>'anesthesiaFee')::numeric as total_price,
'0' as delete_flag,
t.create_time,
t.requester_id as create_by,
1 as tenant_id
FROM temp_new_service_requests t
WHERE t.content_json::jsonb->>'anesthesiaFee' IS NOT NULL
AND (t.content_json::jsonb->>'anesthesiaFee')::numeric > 0;
-- 步骤6: 验证修复结果
-- ============================================================
SELECT
'手术医嘱总数' as ,
COUNT(*) as
FROM wor_service_request
WHERE category_enum = 4
AND delete_flag = '0';
SELECT
'手术收费项目总数' as ,
COUNT(*) as
FROM adm_charge_item ci
WHERE ci.service_table = 'wor_service_request'
AND EXISTS (
SELECT 1 FROM wor_service_request sr
WHERE sr.id = ci.service_id AND sr.category_enum = 4
);
-- 清理临时表
DROP TABLE IF EXISTS temp_new_service_requests;
-- ============================================================
-- ✓ 修复完成!
-- ============================================================

View File

@@ -0,0 +1,254 @@
-- ========================================================
-- Bug #318 历史数据修复脚本
-- 为已存在但未生成手术医嘱的手术申请单补齐数据
-- ========================================================
-- 1. 查询所有未生成手术医嘱的手术申请单
SELECT
rf.id as request_form_id,
rf.prescription_no,
rf.encounter_id,
rf.patient_id,
rf.requester_id,
rf.create_time,
rf.desc_json
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE' -- 手术申请单
AND rf.delete_flag = '0'
AND sr.id IS NULL -- 没有对应的手术医嘱
ORDER BY rf.create_time DESC;
-- ========================================================
-- 2. 修复脚本:为缺失的手术申请单生成手术医嘱
-- ========================================================
-- 创建临时表存储需要修复的数据
CREATE TEMPORARY TABLE IF NOT EXISTS temp_surgery_repair AS
SELECT
rf.id as request_form_id,
rf.prescription_no,
rf.encounter_id,
rf.patient_id,
rf.requester_id,
rf.create_time,
rf.desc_json,
rf.org_id
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL;
-- 查看需要修复的记录数
SELECT COUNT(*) as need_repair_count FROM temp_surgery_repair;
-- ========================================================
-- 3. 插入手术医嘱ServiceRequest
-- ========================================================
INSERT INTO wor_service_request (
id,
bus_no,
prescription_no,
status_enum,
generate_source_enum,
therapy_enum,
quantity,
unit_code,
category_enum,
patient_id,
requester_id,
encounter_id,
authored_time,
org_id,
content_json,
delete_flag,
create_time,
create_by
)
SELECT
nextval('wor_service_request_id_seq'), -- 使用序列生成ID
LPAD(FLOOR(RANDOM() * 10000)::TEXT, 4, '0'), -- 生成4位随机bus_no
prescription_no,
1, -- status_enum: 1-待签发(DRAFT)
1, -- generate_source_enum: 1-医生处方
2, -- therapy_enum: 2-临时医嘱(TEMPORARY)
1, -- quantity: 1
'', -- unit_code
4, -- category_enum: 4-手术
patient_id,
requester_id,
encounter_id,
create_time,
org_id,
CASE
WHEN desc_json IS NOT NULL AND desc_json != '' THEN desc_json
ELSE NULL
END, -- content_json: 存储手术详情
'0', -- delete_flag
create_time,
requester_id
FROM temp_surgery_repair;
-- ========================================================
-- 4. 插入手术收费项目ChargeItem
-- ========================================================
-- 首先创建临时表关联新生成的ServiceRequest
CREATE TEMPORARY TABLE IF NOT EXISTS temp_new_service_request AS
SELECT
sr.id as service_request_id,
sr.prescription_no,
sr.patient_id,
sr.encounter_id,
sr.requester_id,
sr.create_time,
sr.org_id,
sr.bus_no,
t.desc_json
FROM wor_service_request sr
JOIN temp_surgery_repair t ON t.prescription_no = sr.prescription_no
WHERE sr.category_enum = 4
AND sr.delete_flag = '0'
AND sr.create_time > NOW() - INTERVAL '1 hour'; -- 只取最近1小时新增的
-- 插入手术费用收费项目
INSERT INTO adm_charge_item (
id,
bus_no,
status_enum,
generate_source_enum,
patient_id,
context_enum,
encounter_id,
enterer_id,
entered_date,
service_table,
service_id,
product_table,
requesting_org_id,
quantity_value,
quantity_unit,
unit_price,
total_price,
delete_flag,
create_time,
create_by
)
SELECT
nextval('adm_charge_item_id_seq'),
'CI' || bus_no,
1, -- status_enum: 1-草稿(DRAFT)
1, -- generate_source_enum: 1-医生处方
patient_id,
3, -- context_enum: 3-诊疗
encounter_id,
requester_id,
create_time,
'wor_service_request', -- service_table
service_request_id, -- service_id
'wor_activity_definition', -- product_table
org_id,
1, -- quantity_value
'', -- quantity_unit
COALESCE(
(desc_json::jsonb->>'surgeryFee')::numeric,
0
), -- unit_price: 从desc_json解析手术费用
COALESCE(
(desc_json::jsonb->>'surgeryFee')::numeric,
0
), -- total_price
'0',
create_time,
requester_id
FROM temp_new_service_request;
-- 插入麻醉费用收费项目(如果有麻醉费用)
INSERT INTO adm_charge_item (
id,
bus_no,
status_enum,
generate_source_enum,
patient_id,
context_enum,
encounter_id,
enterer_id,
entered_date,
service_table,
service_id,
product_table,
requesting_org_id,
quantity_value,
quantity_unit,
unit_price,
total_price,
delete_flag,
create_time,
create_by
)
SELECT
nextval('adm_charge_item_id_seq'),
'CI' || bus_no || '_A',
1,
1,
patient_id,
3,
encounter_id,
requester_id,
create_time,
'wor_service_request',
service_request_id,
'wor_activity_definition',
org_id,
1,
'',
(desc_json::jsonb->>'anesthesiaFee')::numeric,
(desc_json::jsonb->>'anesthesiaFee')::numeric,
'0',
create_time,
requester_id
FROM temp_new_service_request
WHERE desc_json::jsonb->>'anesthesiaFee' IS NOT NULL
AND (desc_json::jsonb->>'anesthesiaFee')::numeric > 0;
-- ========================================================
-- 5. 验证修复结果
-- ========================================================
-- 查看修复后的手术医嘱数量
SELECT
'修复后的手术医嘱数量' as check_item,
COUNT(*) as count
FROM wor_service_request sr
WHERE sr.category_enum = 4
AND sr.delete_flag = '0'
AND EXISTS (
SELECT 1 FROM doc_request_form rf
WHERE rf.prescription_no = sr.prescription_no
AND rf.type_code = 'PROCEDURE'
);
-- 查看修复后的收费项目数量
SELECT
'修复后的手术收费项目数量' as check_item,
COUNT(*) as count
FROM adm_charge_item ci
WHERE ci.service_table = 'wor_service_request'
AND ci.delete_flag = '0'
AND EXISTS (
SELECT 1 FROM wor_service_request sr
WHERE sr.id = ci.service_id
AND sr.category_enum = 4
);
-- 清理临时表
DROP TABLE IF EXISTS temp_surgery_repair;
DROP TABLE IF EXISTS temp_new_service_request;
-- ========================================================
-- 完成!
-- ========================================================

View File

@@ -0,0 +1,17 @@
-- Bug #318 历史数据修复 - 步骤1查询需要修复的手术申请单
-- 执行此SQL查看有多少手术申请单需要修复
SELECT
rf.id as request_form_id,
rf.prescription_no as ,
rf.encounter_id as ID,
rf.patient_id as ID,
rf.create_time as ,
SUBSTRING(rf.desc_json::text, 1, 100) as
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE' -- 手术申请单
AND rf.delete_flag = '0'
AND sr.id IS NULL -- 没有对应的手术医嘱
ORDER BY rf.create_time DESC;

View File

@@ -0,0 +1,159 @@
-- Bug #318 历史数据修复 - 步骤2执行修复
-- ⚠️ 请在执行前备份数据库!
-- =====================================================
-- 步骤 2.1: 创建临时表存储需要修复的手术申请单
-- =====================================================
CREATE TEMPORARY TABLE temp_repair_list AS
SELECT
rf.id as request_form_id,
rf.prescription_no,
rf.encounter_id,
rf.patient_id,
rf.requester_id,
rf.create_time,
rf.org_id,
rf.desc_json
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL;
-- 查看需要修复的记录数
SELECT COUNT(*) as FROM temp_repair_list;
-- =====================================================
-- 步骤 2.2: 生成手术医嘱ServiceRequest
-- =====================================================
INSERT INTO wor_service_request (
bus_no,
prescription_no,
status_enum,
generate_source_enum,
therapy_enum,
quantity,
unit_code,
category_enum,
patient_id,
requester_id,
encounter_id,
authored_time,
org_id,
content_json,
delete_flag,
create_time,
create_by,
tenant_id
)
SELECT
LPAD(FLOOR(RANDOM() * 10000)::TEXT, 4, '0'),
prescription_no,
1, -- 待签发
1, -- 医生处方
2, -- 临时医嘱
1,
'',
4, -- 手术
patient_id,
requester_id,
encounter_id,
create_time,
org_id,
desc_json,
'0',
create_time,
requester_id,
1 -- tenant_id
FROM temp_repair_list;
-- 查看生成的手术医嘱数量
SELECT '生成的手术医嘱数量' as , COUNT(*) as
FROM wor_service_request
WHERE prescription_no IN (SELECT prescription_no FROM temp_repair_list);
-- =====================================================
-- 步骤 2.3: 生成手术收费项目ChargeItem
-- =====================================================
-- 获取刚生成的ServiceRequest
CREATE TEMPORARY TABLE temp_new_requests AS
SELECT
sr.id as service_request_id,
sr.prescription_no,
sr.patient_id,
sr.encounter_id,
sr.requester_id,
sr.create_time,
sr.org_id,
sr.bus_no,
t.desc_json
FROM wor_service_request sr
JOIN temp_repair_list t ON t.prescription_no = sr.prescription_no
WHERE sr.category_enum = 4
AND sr.delete_flag = '0';
-- 插入手术费用
INSERT INTO adm_charge_item (
bus_no,
status_enum,
generate_source_enum,
patient_id,
context_enum,
encounter_id,
enterer_id,
entered_date,
service_table,
service_id,
product_table,
requesting_org_id,
quantity_value,
quantity_unit,
unit_price,
total_price,
delete_flag,
create_time,
create_by,
tenant_id
)
SELECT
'CI' || bus_no,
1, -- 草稿
1, -- 医生处方
patient_id,
3, -- 诊疗
encounter_id,
requester_id,
create_time,
'wor_service_request',
service_request_id,
'wor_activity_definition',
org_id,
1,
'',
COALESCE((desc_json::jsonb->>'surgeryFee')::numeric, 0),
COALESCE((desc_json::jsonb->>'surgeryFee')::numeric, 0),
'0',
create_time,
requester_id,
1
FROM temp_new_requests;
-- 查看生成的收费项目数量
SELECT '生成的手术收费项目数量' as , COUNT(*) as
FROM adm_charge_item
WHERE service_id IN (SELECT service_request_id FROM temp_new_requests);
-- =====================================================
-- 步骤 2.4: 清理临时表
-- =====================================================
DROP TABLE IF EXISTS temp_repair_list;
DROP TABLE IF EXISTS temp_new_requests;
-- =====================================================
-- 完成!
-- =====================================================