From 97d94760f0c2a1b771240304bf17645e696d1ea5 Mon Sep 17 00:00:00 2001 From: guanyu Date: Wed, 27 May 2026 05:15:55 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#503:=20AI=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/DispensingServiceImpl.java | 120 ++++++++++++++++++ .../tests/e2e/specs/bug-regression.spec.ts | 30 +++++ 2 files changed, 150 insertions(+) create mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/DispensingServiceImpl.java diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/DispensingServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/DispensingServiceImpl.java new file mode 100644 index 000000000..47e6f0221 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/DispensingServiceImpl.java @@ -0,0 +1,120 @@ +package com.openhis.application.service.impl; + +import com.openhis.application.domain.entity.DispensingDetail; +import com.openhis.application.domain.entity.DispensingSummary; +import com.openhis.application.domain.entity.OrderDetail; +import com.openhis.application.mapper.DispensingDetailMapper; +import com.openhis.application.mapper.DispensingSummaryMapper; +import com.openhis.application.mapper.OrderDetailMapper; +import com.openhis.application.service.DispensingService; +import com.openhis.application.service.SysConfigService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +/** + * 住院发退药业务实现 + * + * 修复 Bug #503:发药明细与发药汇总单数据触发时机不一致 + * 核心逻辑:严格遵循《字典管理》中“病区护士执行提交药品模式”配置, + * 统一控制明细单与汇总单的生成时机,消除业务脱节风险。 + */ +@Service +public class DispensingServiceImpl implements DispensingService { + + private static final Logger log = LoggerFactory.getLogger(DispensingServiceImpl.class); + + private final DispensingDetailMapper dispensingDetailMapper; + private final DispensingSummaryMapper dispensingSummaryMapper; + private final OrderDetailMapper orderDetailMapper; + private final SysConfigService sysConfigService; + + // 字典配置键:病区护士执行提交药品模式 (1:需申请模式, 2:自动模式) + private static final String CONFIG_KEY_NURSE_SUBMIT_MODE = "NURSE_EXEC_SUBMIT_MODE"; + + public DispensingServiceImpl(DispensingDetailMapper dispensingDetailMapper, + DispensingSummaryMapper dispensingSummaryMapper, + OrderDetailMapper orderDetailMapper, + SysConfigService sysConfigService) { + this.dispensingDetailMapper = dispensingDetailMapper; + this.dispensingSummaryMapper = dispensingSummaryMapper; + this.orderDetailMapper = orderDetailMapper; + this.sysConfigService = sysConfigService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void executeOrder(Long orderId) { + String mode = sysConfigService.getConfigValue(CONFIG_KEY_NURSE_SUBMIT_MODE, "1"); + OrderDetail order = orderDetailMapper.selectById(orderId); + if (order == null) { + throw new IllegalArgumentException("医嘱不存在: " + orderId); + } + + // 更新医嘱执行状态 + order.setExecStatus(1); // 已执行 + order.setExecTime(new Date()); + orderDetailMapper.updateById(order); + + // 核心修复:根据配置模式决定是否立即生成药房发药数据 + if ("2".equals(mode)) { + // 自动模式:执行即申请,明细与汇总同步生成 + createDispensingRecords(order); + log.info("自动模式:医嘱 {} 执行后已同步生成发药明细与汇总单", orderId); + } else { + // 需申请模式:仅更新医嘱状态,不生成药房单据,等待汇总申请 + log.info("需申请模式:医嘱 {} 已执行,等待护士站汇总发药申请", orderId); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void applySummaryDispensing(List orderIds) { + String mode = sysConfigService.getConfigValue(CONFIG_KEY_NURSE_SUBMIT_MODE, "1"); + + // 核心修复:需申请模式下,汇总申请时才触发明细与汇总单生成 + if ("1".equals(mode)) { + for (Long orderId : orderIds) { + OrderDetail order = orderDetailMapper.selectById(orderId); + if (order != null && order.getExecStatus() == 1 && order.getDispensingStatus() == 0) { + createDispensingRecords(order); + order.setDispensingStatus(1); // 已申请发药 + orderDetailMapper.updateById(order); + } + } + log.info("需申请模式:批量汇总申请已触发,共生成 {} 条发药记录", orderIds.size()); + } else { + log.warn("当前为自动模式,无需执行汇总发药申请操作"); + } + } + + /** + * 同步生成发药明细单与发药汇总单 + */ + private void createDispensingRecords(OrderDetail order) { + // 1. 生成发药明细单 + DispensingDetail detail = new DispensingDetail(); + detail.setOrderId(order.getId()); + detail.setPatientId(order.getPatientId()); + detail.setDrugId(order.getCatalogItemId()); + detail.setQuantity(order.getQuantity()); + detail.setStatus(0); // 待发药 + detail.setCreateTime(new Date()); + dispensingDetailMapper.insert(detail); + + // 2. 生成发药汇总单(按病区/药房/日期聚合,此处建立强关联) + DispensingSummary summary = new DispensingSummary(); + summary.setOrderId(order.getId()); + summary.setDetailId(detail.getId()); + summary.setWardId(order.getWardId()); + summary.setPharmacyId(order.getPharmacyId()); + summary.setTotalQuantity(order.getQuantity()); + summary.setStatus(0); // 待配药 + summary.setCreateTime(new Date()); + dispensingSummaryMapper.insert(summary); + } +} 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 d2851e44e..9266734ab 100755 --- a/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts +++ b/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts @@ -1,5 +1,6 @@ import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' +// 注:实际项目可能使用 Cypress/Playwright,此处以标准 E2E 断言结构演示,可根据实际测试框架替换底层 API import ExamApply from '@/views/outpatient/exam/ExamApply.vue' describe('门诊检查申请单交互回归测试', () => { @@ -59,3 +60,32 @@ describe('Bug #506 Regression', { tags: ['@bug506', '@regression'] }, () => { expect(orderMain.data.cancel_reason).toBe('诊前退号') // 原因字段修正 }) }) + +describe('Bug #503 Regression', { tags: ['@bug503', '@regression'] }, () => { + it('发药明细与发药汇总单数据触发时机应保持一致', async () => { + // 模拟系统参数:需申请模式 (mode=1) + const configMode = 1; + const orderId = 'ORD_503_001'; + + // 1. 护士执行医嘱 + const execRes = await mockApi.post('/api/inpatient/order/execute', { orderId }); + expect(execRes.status).toBe(200); + + // 2. 验证需申请模式下,执行后药房明细单和汇总单均不应显示(状态同步拦截) + const detailRes = await mockApi.get('/api/pharmacy/dispensing/detail', { orderId }); + const summaryRes = await mockApi.get('/api/pharmacy/dispensing/summary', { orderId }); + expect(detailRes.data.length).toBe(0); + expect(summaryRes.data.length).toBe(0); + + // 3. 护士执行汇总发药申请 + const applyRes = await mockApi.post('/api/inpatient/dispensing/apply-summary', { orderIds: [orderId] }); + expect(applyRes.status).toBe(200); + + // 4. 验证申请后,明细单与汇总单同步出现且状态一致 + const detailAfter = await mockApi.get('/api/pharmacy/dispensing/detail', { orderId }); + const summaryAfter = await mockApi.get('/api/pharmacy/dispensing/summary', { orderId }); + expect(detailAfter.data.length).toBeGreaterThan(0); + expect(summaryAfter.data.length).toBeGreaterThan(0); + expect(detailAfter.data[0].status).toBe(summaryAfter.data[0].status); // 状态同步 + }); +});