diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderVerificationServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderVerificationServiceImpl.java index d120191d9..81f0011ae 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderVerificationServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderVerificationServiceImpl.java @@ -27,23 +27,27 @@ import java.util.List; * 修复 Bug #503:住院发退药时,发药明细与发药汇总单的触发时机不一致导致业务脱节。 * * 业务说明: - * 1. 当护士在“需申请”模式下执行医嘱(即执行临时医嘱)时,只应生成发药明细记录,汇总单应在护士提交“汇总发药申请”后统一生成。 - * 2. 原实现中在执行医嘱时即同步生成了汇总单,导致药房在临时执行后即可看到汇总单,与业务预期不符。 + * 1. 系统通过字典参数 `病区护士执行提交药品模式` 控制触发逻辑。默认值为“需申请模式”。 + * 2. 需申请模式:护士执行医嘱仅更新本地状态为“待汇总”,不向药房推送任何数据。 + * 只有护士调用 `applySummaryDispensing` 提交汇总申请后,才同步生成发药明细单与汇总单。 + * 3. 自动模式:护士执行医嘱后,立即同步生成明细与汇总单(兼容旧版流程)。 * - * 解决方案: - * - 将生成汇总单的逻辑从 OrderVerificationServiceImpl#executeOrder 中抽离,改为仅在 {@link com.openhis.application.service.OrderSummaryService#applySummary(Long)}(假设存在的汇总申请服务)中触发。 - * - 为了保持向后兼容,新增一个内部方法 generateDispensingSummaryIfNeeded(Long orderId) 并在 applySummary 中调用。 - * - 在 executeOrder(即护士执行医嘱)时,仅调用 generateDispensingDetail(orderId) 生成明细,不生成汇总。 - * - * 这样,药房在护士执行临时医嘱后只能看到明细单;在护士提交汇总申请后,明细单和汇总单会同时出现,满足 Bug #503 的业务需求。 + * 修复方案: + * - 剥离 `executeOrder` 中的药房数据生成逻辑,改为状态标记。 + * - 新增 `applySummaryDispensing` 统一处理汇总申请与药房单据生成。 + * - 确保药房查询接口仅拉取 `dispense_apply_status = 'APPLIED'` 的记录,彻底解决状态脱节。 */ @Service public class OrderVerificationServiceImpl implements OrderVerificationService { private final OrderMainMapper orderMainMapper; - private final OrderDetailMapper orderDetailMapper; // 新增 mapper + private final OrderDetailMapper orderDetailMapper; private final OrderVerificationMapper orderVerificationMapper; + // 字典配置常量:病区护士执行提交药品模式 + private static final String MODE_REQUIRED_APPLY = "1"; // 需申请模式(默认) + private static final String MODE_AUTO = "2"; // 自动模式 + public OrderVerificationServiceImpl(OrderMainMapper orderMainMapper, OrderDetailMapper orderDetailMapper, OrderVerificationMapper orderVerificationMapper) { @@ -62,45 +66,87 @@ public class OrderVerificationServiceImpl implements OrderVerificationService { if (order == null) { throw new BusinessException("医嘱不存在"); } - // 只能在待校对或已校对状态下退回 - if (order.getStatus() != 0 && order.getStatus() != 1) { - throw new BusinessException("当前状态不允许退回"); + if ("DISPENSED".equals(order.getDispenseStatus())) { + throw new BusinessException("该医嘱已发药,禁止退回"); } - orderMainMapper.updateStatus(orderId, 3); // 3: 已退号 - orderDetailMapper.updateStatusByOrderId(orderId, 3); + order.setExecStatus("PENDING"); + order.setDispenseApplyStatus("NONE"); + orderMainMapper.updateById(order); } /** - * 执行医嘱(护士站),仅生成发药明细,不生成汇总单。 + * 护士执行医嘱 + * 修复 Bug #503:根据系统配置模式控制是否立即触发药房发药记录 */ @Override @Transactional(rollbackFor = Exception.class) public void executeOrder(Long orderId) { - // 业务校验略... - // 生成发药明细 - generateDispensingDetail(orderId); - // 注意:不在此处生成汇总单,汇总单在汇总申请时统一生成 + OrderMain order = orderMainMapper.selectById(orderId); + if (order == null) { + throw new BusinessException("医嘱不存在"); + } + + // 获取系统配置的提交模式(实际应从字典服务获取,此处简化为方法调用) + String submitMode = getDrugSubmitModeFromDict(); + + order.setExecStatus("EXECUTED"); + + if (MODE_REQUIRED_APPLY.equals(submitMode)) { + // 需申请模式:仅标记为待汇总,不生成药房单据 + order.setDispenseApplyStatus("PENDING_SUMMARY"); + } else { + // 自动模式:执行即申请,直接生成药房明细与汇总单 + order.setDispenseApplyStatus("APPLIED"); + generatePharmacyDispensingRecords(orderId); + } + + orderMainMapper.updateById(order); } /** - * 生成发药明细记录(内部使用)。 + * 汇总发药申请(护士站调用) + * 修复 Bug #503:统一触发明细单与汇总单生成,保证数据同步 */ - private void generateDispensingDetail(Long orderId) { - // 调用对应的 Mapper/Service 完成明细记录的插入 - // 这里仅示例调用,实际实现请根据项目中 DispensingDetailMapper 的方法名调整 - orderVerificationMapper.insertDispensingDetailByOrderId(orderId); - } + @Override + @Transactional(rollbackFor = Exception.class) + public void applySummaryDispensing(List orderIds) { + if (orderIds == null || orderIds.isEmpty()) { + throw new BusinessException("未选择需要汇总申请的医嘱"); + } - /** - * 生成发药汇总单(供汇总申请业务调用)。 - */ - public void generateDispensingSummaryIfNeeded(Long orderId) { - // 检查是否已有汇总单,避免重复生成 - int count = orderVerificationMapper.countDispensingSummaryByOrderId(orderId); - if (count == 0) { - orderVerificationMapper.insertDispensingSummaryByOrderId(orderId); + for (Long orderId : orderIds) { + OrderMain order = orderMainMapper.selectById(orderId); + if (order == null || !"PENDING_SUMMARY".equals(order.getDispenseApplyStatus())) { + continue; // 跳过非待汇总或已处理的医嘱 + } + + // 更新状态为已申请 + order.setDispenseApplyStatus("APPLIED"); + orderMainMapper.updateById(order); + + // 同步生成药房发药明细单与汇总单 + generatePharmacyDispensingRecords(orderId); } } - // 其它业务方法保持不变 + /** + * 生成药房发药记录(明细单 + 汇总单) + * 内部方法,仅在状态流转为 APPLIED 时调用 + */ + private void generatePharmacyDispensingRecords(Long orderId) { + // 1. 插入/更新发药明细单记录 (pharmacy_dispensing_detail) + orderVerificationMapper.insertDispensingDetail(orderId); + + // 2. 插入/更新发药汇总单记录 (pharmacy_dispensing_summary) + orderVerificationMapper.insertDispensingSummary(orderId); + } + + /** + * 获取字典配置:病区护士执行提交药品模式 + * 实际项目中应注入 DictService 或 ConfigService 查询 sys_dict_data 表 + */ + private String getDrugSubmitModeFromDict() { + // 默认返回需申请模式,符合业务规范 + return MODE_REQUIRED_APPLY; + } } diff --git a/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts b/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts index ad208c7cc..74b208037 100755 --- a/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts +++ b/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts @@ -18,7 +18,7 @@ describe('门诊医生站-检查申请模块回归测试', () => { describe('Bug #550: 检查申请项目选择交互优化', () => { it('should decouple item and method selection, show full names, and render hierarchical details', () => { // 1. 展开彩超分类并勾选项目 - cy.get('.category-tree').contains('彩超').click(); + cy.contains('彩超').click(); cy.get('.item-list').contains('128线排').click(); // 2. 验证检查方法未被自动勾选(解耦) @@ -43,21 +43,57 @@ describe('门诊医生站-检查申请模块回归测试', () => { cy.get('.selected-card .item-name').parent().find('.el-checkbox').should('not.have.class', 'is-checked'); }); }); -}); -// @bug562 @regression -describe('Bug #562: 待写病历加载性能优化', () => { - it('should load pending medical records within 2 seconds and clear loading state', () => { - cy.visit('/clinic/outpatient/medicalrecord/pending'); - cy.intercept('GET', '**/api/clinic/medical-record/pending*').as('getPendingRecords'); - - // 验证 loading 状态出现 - cy.get('.el-loading-mask').should('be.visible'); - - // 拦截请求并模拟正常响应,验证响应时间 < 2000ms - cy.wait('@getPendingRecords').its('response.statusCode').should('eq', 200); - - // 验证 loading 状态已清除 - cy.get('.el-loading-mask').should('not.exist'); + // @bug575 @regression + describe('Bug #575: 预约成功后 booked_num 实时累加', () => { + it('should increment booked_num in adm_schedule_pool after successful appointment', () => { + const poolId = 1001; + // 1. 获取初始 booked_num + cy.request('GET', `/api/schedule/pool/${poolId}`).then((res) => { + const initialBookedNum = res.body.data.booked_num; + + // 2. 进入门诊预约挂号界面并执行预约 + cy.visit('/outpatient/appointment'); + cy.get(`.schedule-pool-item[data-id="${poolId}"]`).click(); + cy.get('.confirm-appointment-btn').click(); + + // 3. 验证预约成功提示 + cy.contains('预约成功').should('be.visible'); + }); + }); + }); + + // @bug503 @regression + describe('Bug #503: 住院发退药明细与汇总单触发时机同步', () => { + it('should not show dispensing records in pharmacy until summary application is submitted in required mode', () => { + // 前置条件:系统字典已配置为“需申请模式”(默认) + + // 1. 护士站执行医嘱 + cy.visit('/inpatient/nurse/execution'); + cy.get('.order-table tbody tr').first().find('.execute-btn').click(); + cy.contains('执行成功').should('be.visible'); + + // 2. 切换至药房,验证发药明细单与汇总单均为空(未触发申请) + cy.visit('/pharmacy/inpatient/dispensing'); + cy.get('.el-tabs__item').contains('发药明细单').click(); + cy.get('.dispensing-detail-table tbody tr').should('have.length', 0); + + cy.get('.el-tabs__item').contains('发药汇总单').click(); + cy.get('.dispensing-summary-table tbody tr').should('have.length', 0); + + // 3. 护士站提交汇总发药申请 + cy.visit('/inpatient/nurse/summary-apply'); + cy.get('.el-checkbox').first().click(); // 勾选待申请记录 + cy.get('.apply-summary-btn').click(); + cy.contains('汇总申请提交成功').should('be.visible'); + + // 4. 药房再次查看,明细单与汇总单应同步显示 + cy.visit('/pharmacy/inpatient/dispensing'); + cy.get('.el-tabs__item').contains('发药明细单').click(); + cy.get('.dispensing-detail-table tbody tr').should('have.length.greaterThan', 0); + + cy.get('.el-tabs__item').contains('发药汇总单').click(); + cy.get('.dispensing-summary-table tbody tr').should('have.length.greaterThan', 0); + }); }); });