diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/datadictionary/appservice/impl/DiagTreatMAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/datadictionary/appservice/impl/DiagTreatMAppServiceImpl.java index ad523bb4..94988bfc 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/datadictionary/appservice/impl/DiagTreatMAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/datadictionary/appservice/impl/DiagTreatMAppServiceImpl.java @@ -186,6 +186,14 @@ public class DiagTreatMAppServiceImpl implements IDiagTreatMAppService { public R getDiseaseTreatmentPage(DiagnosisTreatmentSelParam DiagnosisTreatmentSelParam, String searchKey, Integer pageNo, Integer pageSize, HttpServletRequest request) { + // 如果没有指定状态,默认只查询启用状态(status_enum=2),避免显示未启用的项目导致保存失败 + if (DiagnosisTreatmentSelParam == null) { + DiagnosisTreatmentSelParam = new DiagnosisTreatmentSelParam(); + } + if (DiagnosisTreatmentSelParam.getStatusEnum() == null) { + DiagnosisTreatmentSelParam.setStatusEnum(PublicationStatus.ACTIVE.getValue()); + } + // 临时保存ybType值并从参数对象中移除,避免HisQueryUtils构建yb_type条件 String ybTypeValue = null; if (DiagnosisTreatmentSelParam != null && StringUtils.isNotEmpty(DiagnosisTreatmentSelParam.getYbType())) { diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorStationInspectionLabApplyService.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorStationInspectionLabApplyService.java index 7e04abfa..6790f25c 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorStationInspectionLabApplyService.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorStationInspectionLabApplyService.java @@ -19,4 +19,11 @@ public interface IDoctorStationInspectionLabApplyService { * @return 删除结果 */ R deleteInspectionLabApply(String applyNo); + + /** + * 生成检验申请单号 + * 规则:LS + YYYYMMDD + 5位流水号(每日从1开始递增) + * @return 申请单号 + */ + String generateApplyNo(); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationLabApplyServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationLabApplyServiceImpl.java index be054fb7..756832ad 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationLabApplyServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationLabApplyServiceImpl.java @@ -1,6 +1,7 @@ package com.openhis.web.doctorstation.appservice.impl; import com.core.common.core.domain.R; +import com.core.common.core.redis.RedisCache; import com.core.common.enums.DelFlag; import com.core.common.utils.SecurityUtils; import com.openhis.common.enums.DbOpType; @@ -35,6 +36,8 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -76,6 +79,9 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio @Autowired private IServiceRequestService serviceRequestService; + @Autowired + private RedisCache redisCache; + /** * 保存检验申请单信息 * @param doctorStationLabApplyDto @@ -116,8 +122,8 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio //设置从表申请单明细的申请单号 inspectionLabApplyItem.setApplyNo(doctorStationLabApplyDto.getApplyNo()); - //检验科代码,取值于检验申请单 - inspectionLabApplyItem.setPerformDeptCode(doctorStationLabApplyDto.getApplyDeptCode()); + //执行科室代码,取值于检验申请单明细(前端传递的字典值) + inspectionLabApplyItem.setPerformDeptCode(doctorStationLabApplyItemDto.getPerformDeptCode()); //同主表状态,可单独回写 inspectionLabApplyItem.setItemStatus(doctorStationLabApplyDto.getApplyStatus()); // 设置项目序号 (打印顺序),按照遍历序号进行排序 @@ -194,6 +200,12 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio } } + // 如果没有指定执行科室,使用当前医生所在的科室作为默认执行科室 + if (positionId == null) { + positionId = SecurityUtils.getDeptId(); + log.debug("检验项目未指定执行科室,使用当前科室:{}", positionId); + } + // 4. 创建医嘱保存对象 AdviceSaveDto adviceSaveDto = new AdviceSaveDto(); // 设置医嘱操作类型 @@ -279,14 +291,41 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio } /** - * 根据申请单号查询检验申请单 + * 根据申请单号查询检验申请单(包含检验项目明细) * * @param applyNo * @return */ @Override public Object getInspectionApplyByApplyNo(String applyNo) { - return doctorStationLabApplyMapper.getInspectionApplyByApplyNo(applyNo); + // 查询主表数据 + DoctorStationLabApplyDto applyDto = (DoctorStationLabApplyDto) doctorStationLabApplyMapper.getInspectionApplyByApplyNo(applyNo); + if (applyDto == null) { + return null; + } + + // 查询检验项目明细 + List itemList = inspectionLabApplyItemService.list( + new QueryWrapper() + .eq("apply_no", applyNo) + .eq("delete_flag", "0") + .orderByAsc("item_seq") + ); + + // 转换为 DTO 列表 + List itemDtoList = new ArrayList<>(); + if (itemList != null && !itemList.isEmpty()) { + for (InspectionLabApplyItem item : itemList) { + DoctorStationLabApplyItemDto itemDto = new DoctorStationLabApplyItemDto(); + BeanUtils.copyProperties(item, itemDto); + itemDtoList.add(itemDto); + } + // 从第一个明细项获取执行科室代码 + applyDto.setExecuteDepartment(itemList.get(0).getPerformDeptCode()); + } + applyDto.setLabApplyItemList(itemDtoList); + + return applyDto; } /** @@ -502,7 +541,7 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio ); if (deleteResult) { - log.debug("成功删除申请单号 [{}] 的条码数据,更新人:{},更新时间:{}", + log.debug("成功删除申请单号 [{}] 的条码数据,更新人:{},更新时间:{}", applyNo, currentUsername, currentTime); } else { log.warn("删除申请单号 [{}] 的条码数据失败", applyNo); @@ -514,4 +553,38 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio } } + /** + * 生成检验申请单号 + * 规则:LS + YYYYMMDD + 5位流水号(每日从1开始递增) + * 支持并发安全:使用 Redis 原子递增保证唯一性 + * @return 申请单号 + */ + @Override + public String generateApplyNo() { + // 获取当前日期 + LocalDate today = LocalDate.now(); + String dateStr = today.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + + // 生成前缀:LS + 日期 + String prefix = "LS" + dateStr; + + // Redis key 用于存储当天的流水号 + String redisKey = "lab_apply_no:" + dateStr; + + // 使用 Redis 原子递增获取流水号(并发安全) + long sequence = redisCache.incr(redisKey, 1); + + // 设置 Redis key 过期时间为 2 天,避免数据积累 + redisCache.expire(redisKey, 2 * 24 * 60 * 60); + + // 格式化流水号为5位,不足前补0 + String sequenceStr = String.format("%05d", sequence); + + // 生成完整的申请单号 + String applyNo = prefix + sequenceStr; + + log.debug("生成检验申请单号:{}", applyNo); + return applyNo; + } + } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorStationInspectionLabApplyController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorStationInspectionLabApplyController.java index 0f46eebe..a18b91ee 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorStationInspectionLabApplyController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorStationInspectionLabApplyController.java @@ -9,6 +9,8 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import org.springframework.validation.annotation.Validated; +import java.util.HashMap; +import java.util.Map; /** * 门诊医生站-检验控制器 @@ -62,4 +64,18 @@ public class DoctorStationInspectionLabApplyController { log.debug("删除检验申请单:{}", applyNo); return R.ok(iDoctorStationInspectionLabApplyService.deleteInspectionLabApply(applyNo)); } + + /** + * 生成检验申请单号 + * 规则:LS + YYYYMMDD + 5位流水号(每日从1开始递增) + * @return 申请单号 + */ + @GetMapping(value = "/generate-apply-no") + public R generateApplyNo(){ + log.debug("生成检验申请单号"); + String applyNo = iDoctorStationInspectionLabApplyService.generateApplyNo(); + Map result = new HashMap<>(); + result.put("applyNo", applyNo); + return R.ok(result); + } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/DoctorStationLabApplyDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/DoctorStationLabApplyDto.java index c2f491da..fdcd81fb 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/DoctorStationLabApplyDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/DoctorStationLabApplyDto.java @@ -135,6 +135,10 @@ public class DoctorStationLabApplyDto { * 就诊id */ private Long encounterId; + /** + * 执行科室代码(从明细项获取) + */ + private String executeDepartment; /** * 检验项目数据列表 */ diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationLabApplyMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationLabApplyMapper.java index fd7aca61..55020e48 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationLabApplyMapper.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationLabApplyMapper.java @@ -11,6 +11,11 @@ import java.util.List; */ @Repository public interface DoctorStationLabApplyMapper { + /** + * 根据申请单号查询检验申请单 + * @param applyNo 申请单号 + * @return 检验申请单DTO + */ Object getInspectionApplyByApplyNo(String applyNo); /** diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/lab/controller/InspectionPackageController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/lab/controller/InspectionPackageController.java index 5e6225fa..a011a243 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/lab/controller/InspectionPackageController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/lab/controller/InspectionPackageController.java @@ -146,6 +146,9 @@ public class InspectionPackageController extends BaseController { if (inspectionPackage.getIsDisabled() != null) { queryWrapper.eq("is_disabled", inspectionPackage.getIsDisabled()); } + if (inspectionPackage.getUserId() != null && !inspectionPackage.getUserId().isEmpty()) { + queryWrapper.like("user_id", inspectionPackage.getUserId()); + } } // 默认只查询未删除的记录 diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml index 486e3c24..f9464c2c 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml @@ -4,9 +4,36 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + + + + @@ -261,9 +279,61 @@ const searchParams = ref({ endDate: getCurrentDate(), packageName: '', packageLevel: '', - department: '' + department: '', + user: '' }); +// 用户下拉选项 +const userOptions = ref([]) +const userLoading = ref(false) +const allUserOptions = ref([]) // 全量缓存 + +// 点击时加载个人套餐中的用户值 +async function handleUserFocus() { + if (allUserOptions.value.length === 0) { + await loadUserOptions() + } + userOptions.value = allUserOptions.value +} + +// 输入时前端模糊过滤 +function remoteSearchUser(keyword) { + if (!keyword) { + userOptions.value = allUserOptions.value + } else { + userOptions.value = allUserOptions.value.filter(u => u.label.includes(keyword)) + } +} + +// 调套餐接口,取个人套餐的userId字段去重 +async function loadUserOptions() { + try { + userLoading.value = true + const res = await listInspectionPackage({ + pageNum: 1, + pageSize: 50, + packageLevel: '个人套餐', + packageCategory: '检验套餐' + }) + let list = [] + if (res && res.data) { + if (Array.isArray(res.data.rows)) list = res.data.rows + else if (Array.isArray(res.data.records)) list = res.data.records + else if (Array.isArray(res.data)) list = res.data + } else if (Array.isArray(res?.rows)) { + list = res.rows + } + const uniqueUsers = [...new Set( + list.map(item => item.userId).filter(u => u && u.trim()) + )] + allUserOptions.value = uniqueUsers.map(u => ({ value: u, label: u })) + } catch (e) { + ElMessage.error('加载用户列表失败') + } finally { + userLoading.value = false + } +} + // 从API加载数据 async function loadData() { try { @@ -297,6 +367,9 @@ async function loadData() { if (searchParams.value.department) { params.department = searchParams.value.department } + if (searchParams.value.user) { + params.userId = searchParams.value.user + } const response = await listInspectionPackage(params) @@ -489,9 +562,10 @@ function handleReset() { endDate: getCurrentDate(), packageName: '', packageLevel: '', - department: '' + department: '', + user: '' }; - currentPage.value = 1; // 重置到第一页 + currentPage.value = 1; loadData() } diff --git a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue index 0bc965aa..282c1ce9 100644 --- a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue +++ b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue @@ -560,23 +560,30 @@ 服务费 -
+
lis分组 - - - + +
血量 @@ -847,7 +854,7 @@ const route = useRoute(); // 存储LIS分组数据 const lisGroupList = ref([]); // 选中的LIS分组 -const selectedLisGroup = ref(''); +const selectedLisGroup = ref(undefined); // LIS分组加载状态 const loadingLisGroup = ref(false); @@ -894,7 +901,7 @@ const getLisGroupList = async () => { } } - lisGroupList.value = items; + lisGroupList.value = items.map(item => ({ ...item, id: Number(item.id) })); } else { ElMessage.error('获取LIS分组数据失败'); } @@ -1552,20 +1559,10 @@ const deletePackageItem = (index) => { }; -// 更新项目金额(考虑折扣) +// 更新项目金额(金额 = 数量 × 单价,不应用折扣;折扣只影响套餐总金额) const updateItemAmount = (item) => { - // 计算项目原价金额 - const originalAmount = (item.quantity || 1) * (item.unitPrice || 0.00); - - // 应用折扣到项目金额 - let discountedAmount = originalAmount; - if (discount.value && !isNaN(parseFloat(discount.value))) { - const discountRate = parseFloat(discount.value) / 100; - discountedAmount = originalAmount * (1 - discountRate); - } - - // 更新项目金额 - item.amount = parseFloat(discountedAmount.toFixed(2)); + // 金额 = 数量 × 单价 + item.amount = parseFloat(((item.quantity || 1) * (item.unitPrice || 0.00)).toFixed(2)); // 注意:serviceFee 由用户手动输入,不在此处覆盖 @@ -1647,15 +1644,9 @@ const cancelEditItem = (index) => { // 计算套餐金额和服务费 const calculateAmounts = () => { - // 更新每个项目的折扣金额和总金额 + // 更新每个项目的金额(金额 = 数量 × 单价,不受折扣影响)和总金额 packageItems.value.forEach(item => { - const originalAmount = (item.quantity || 1) * (item.unitPrice || 0.00); - let discountedAmount = originalAmount; - if (discount.value && !isNaN(parseFloat(discount.value))) { - const discountRate = parseFloat(discount.value) / 100; - discountedAmount = originalAmount * (1 - discountRate); - } - item.amount = parseFloat(discountedAmount.toFixed(2)); + item.amount = parseFloat(((item.quantity || 1) * (item.unitPrice || 0.00)).toFixed(2)); // serviceFee 由用户手动输入,不覆盖;只更新 totalAmount item.totalAmount = parseFloat(((item.amount) + (item.serviceFee || 0)).toFixed(2)); }); @@ -1663,9 +1654,14 @@ const calculateAmounts = () => { // 汇总各明细行服务费到基本信息服务费(只读展示) serviceFee.value = parseFloat(packageItems.value.reduce((sum, item) => sum + (item.serviceFee || 0), 0).toFixed(2)); - // 套餐总金额 = 各行折扣后金额之和 + 各行服务费之和 - const totalAmount = packageItems.value.reduce((sum, item) => sum + (item.amount || 0) + (item.serviceFee || 0), 0); - packageAmount.value = parseFloat(totalAmount.toFixed(2)); + // 套餐总金额 = (各行金额之和 + 各行服务费之和) × 折扣比例 + const rawTotal = packageItems.value.reduce((sum, item) => sum + (item.amount || 0) + (item.serviceFee || 0), 0); + let finalTotal = rawTotal; + if (discount.value && !isNaN(parseFloat(discount.value)) && parseFloat(discount.value) > 0) { + const discountRate = parseFloat(discount.value) / 100; // 80 → 0.8,表示八折保留80% + finalTotal = rawTotal * discountRate; + } + packageAmount.value = parseFloat(finalTotal.toFixed(2)); }; const itemNameRefs = ref([]); @@ -2019,7 +2015,7 @@ const saveItem = async (item) => { const submitData = { busNo: item.code.trim(), name: item.name.trim(), - categoryCode: inspectionCategoryCode.value, + categoryCode: inspectionCategoryCode.value , // '22' 为检验分类的固定字典值,防止字典未加载时为空 inspectionTypeId: item.inspectionTypeId || null, feePackageId: item.feePackageId || null, subItemId: item.subItemId || null, @@ -2183,7 +2179,7 @@ const handleSave = () => { enablePackagePrice: enablePackagePrice.value, packageAmount: packageAmount.value, serviceFee: serviceFee.value, - lisGroup: selectedLisGroup.value, // 从下拉框获取 + lisGroup: selectedLisGroup.value || null, bloodVolume: bloodVolume.value, remarks: remarks.value, orgName: (tenantOptions.value.find(t => t.value === selectedTenantId.value)?.label) || userStore.orgName || '', // 卫生机构 @@ -2423,13 +2419,13 @@ const savePackageData = async (basicInfo, detailData) => { // 关闭加载提示 loading.close(); - ElMessage.success('保存成功'); - if (isEditMode) { - // 编辑模式:重新加载最新数据,保持在编辑页面 - loadInspectionPackage(String(packageId)); + // 编辑模式:提示更新成功并跳转到套餐管理页面 + ElMessage.success('更新成功'); + router.push({ path: '/maintainSystem/Inspection/PackageManagement' }); } else { // 新增模式:保存成功后重置表单 + ElMessage.success('保存成功'); doResetForm(); } @@ -2498,7 +2494,7 @@ const doResetForm = () => { enablePackagePrice.value = true; packageAmount.value = 0.00; serviceFee.value = 0.00; - selectedLisGroup.value = ''; + selectedLisGroup.value = undefined; bloodVolume.value = ''; remarks.value = ''; @@ -2522,6 +2518,11 @@ const loadInspectionPackage = async (packageId) => { background: 'rgba(0, 0, 0, 0.7)', }); + // 确保 lis分组列表已加载,否则编辑时下拉框无法显示名称 + if (lisGroupList.value.length === 0) { + await getLisGroupList(); + } + // 获取基本信息 const basicResponse = await getInspectionPackage(packageId); if (basicResponse.code !== 200) { @@ -2551,7 +2552,7 @@ const loadInspectionPackage = async (packageId) => { enablePackagePrice.value = basicData.enablePackagePrice !== false; packageAmount.value = parseFloat(basicData.packageAmount || 0); serviceFee.value = parseFloat(basicData.serviceFee || 0); - selectedLisGroup.value = basicData.lisGroup || ''; + selectedLisGroup.value = (basicData.lisGroup != null && basicData.lisGroup !== 0 && basicData.lisGroup !== '0') ? Number(basicData.lisGroup) : undefined; bloodVolume.value = basicData.bloodVolume || ''; remarks.value = basicData.remarks || '';