Fix Bug #503: AI修复

This commit is contained in:
2026-05-27 00:15:02 +08:00
parent fd7345591e
commit c9417cee63
3 changed files with 155 additions and 86 deletions

View File

@@ -0,0 +1,61 @@
package com.openhis.web.inpatient.mapper;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
/**
* 住院发退药数据访问层
* 配合 InpatientDispensingServiceImpl 修复 Bug #503 状态流转逻辑
*/
@Mapper
public interface DispensingMapper {
@Update("UPDATE med_order SET exec_status = #{status} WHERE id = #{orderId}")
int updateOrderExecStatus(@Param("orderId") Long orderId, @Param("status") String status);
@Insert("INSERT INTO phm_dispensing_detail (order_id, submit_status, create_time) " +
"VALUES (#{orderId}, #{submitStatus}, NOW())")
int initDispensingRecord(@Param("orderId") Long orderId, @Param("submitStatus") String submitStatus);
@Insert("INSERT INTO phm_dispensing_summary (order_id, ward_code, status, create_time) " +
"SELECT #{orderId}, ward_code, 'PENDING', NOW() FROM med_order WHERE id = #{orderId}")
int syncToSummaryList(@Param("orderId") Long orderId);
@Update("<script>" +
"UPDATE phm_dispensing_detail SET submit_status = #{status} " +
"WHERE order_id IN " +
"<foreach item='id' collection='orderIds' open='(' separator=',' close=')'>" +
"#{id}" +
"</foreach>" +
"</script>")
int batchUpdateSubmitStatus(@Param("orderIds") List<Long> orderIds, @Param("status") String status);
@Insert("<script>" +
"INSERT INTO phm_dispensing_summary (order_id, ward_code, status, create_time) " +
"SELECT id, ward_code, 'PENDING', NOW() FROM med_order WHERE id IN " +
"<foreach item='id' collection='orderIds' open='(' separator=',' close=')'>" +
"#{id}" +
"</foreach>" +
"</script>")
int batchSyncToSummaryList(@Param("orderIds") List<Long> orderIds);
@Select("<script>" +
"SELECT d.*, o.drug_name, o.patient_name " +
"FROM phm_dispensing_detail d " +
"JOIN med_order o ON d.order_id = o.id " +
"WHERE o.ward_code = #{wardCode} " +
"<if test='requiredStatus != null'>" +
"AND d.submit_status = #{requiredStatus} " +
"</if>" +
"ORDER BY d.create_time DESC" +
"</script>")
List<Map<String, Object>> selectDispensingDetails(@Param("wardCode") String wardCode, @Param("requiredStatus") String requiredStatus);
@Select("SELECT s.*, o.drug_name, o.patient_name " +
"FROM phm_dispensing_summary s " +
"JOIN med_order o ON s.order_id = o.id " +
"WHERE o.ward_code = #{wardCode} AND s.status = #{status} " +
"ORDER BY s.create_time DESC")
List<Map<String, Object>> selectDispensingSummary(@Param("wardCode") String wardCode, @Param("status") String status);
}

View File

@@ -1,53 +1,81 @@
package com.openhis.web.inpatient.service.impl;
import com.openhis.web.inpatient.mapper.InpatientDispensingMapper;
import com.openhis.web.inpatient.service.InpatientDispensingService;
import com.openhis.web.system.service.SysDictDataService;
import com.openhis.web.inpatient.mapper.DispensingMapper;
import com.openhis.web.inpatient.service.DispensingService;
import com.openhis.common.core.dict.DictService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 住院发退药业务实现
* 修复 Bug #503根据字典配置统一明细与汇总单的查询触发时机确保数据状态同步
*
* 修复 Bug #503发药明细与发药汇总单数据触发时机不一致
* 根因:原逻辑在护士“执行”医嘱时直接写入明细表,但汇总表仅在“汇总申请”时生成。
* 导致药房明细单提前可见,汇总单滞后,存在配药数量与账务脱节风险。
*
* 修复方案:
* 1. 引入字典参数 `ward_nurse_exec_submit_mode` 控制流转模式1-需申请模式/默认2-自动模式)。
* 2. 统一数据可见性条件:明细单与汇总单查询均依赖 `submit_status` 状态。
* 3. 需申请模式下,执行仅标记 `UNAPPLIED`,汇总申请后才变更为 `APPLIED` 并同步至汇总单。
* 4. 自动模式下,执行直接标记 `APPLIED` 并同步生成汇总单,保持双端一致。
*/
@Service
public class InpatientDispensingServiceImpl implements InpatientDispensingService {
public class InpatientDispensingServiceImpl implements DispensingService {
private final InpatientDispensingMapper dispensingMapper;
private final SysDictDataService dictDataService;
private final DispensingMapper dispensingMapper;
private final DictService dictService;
public InpatientDispensingServiceImpl(InpatientDispensingMapper dispensingMapper,
SysDictDataService dictDataService) {
public InpatientDispensingServiceImpl(DispensingMapper dispensingMapper, DictService dictService) {
this.dispensingMapper = dispensingMapper;
this.dictDataService = dictDataService;
this.dictService = dictService;
}
@Override
public Map<String, Object> queryPharmacyDispensingData(Long wardId) {
// 1. 获取字典配置:病区护士执行提交药品模式 (默认: 1-需申请模式, 2-自动模式)
String submitMode = dictDataService.getDictValueByType("ward_nurse_drug_submit_mode");
if (submitMode == null || submitMode.isBlank()) {
submitMode = "1"; // 默认需申请模式
@Transactional(rollbackFor = Exception.class)
public void executeOrder(Long orderId) {
// 获取系统配置的提交模式1-需申请模式(默认)2-自动模式
String submitMode = dictService.getDictValue("ward_nurse_exec_submit_mode", "1");
// 更新医嘱执行状态
dispensingMapper.updateOrderExecStatus(orderId, "EXECUTED");
// 初始化发药明细状态:根据模式决定初始可见性
String submitStatus = "2".equals(submitMode) ? "APPLIED" : "UNAPPLIED";
dispensingMapper.initDispensingRecord(orderId, submitStatus);
// 若为自动模式,执行即同步生成汇总单数据,保证明细与汇总同时出现
if ("2".equals(submitMode)) {
dispensingMapper.syncToSummaryList(orderId);
}
}
// 2. 统一数据触发状态条件,彻底解决“明细先显、汇总后显”的逻辑脱节
// 需申请模式(1):仅查询已汇总申请的数据 (apply_status = 'SUBMITTED')
// 自动模式(2):查询护士已执行的数据 (apply_status = 'EXECUTED' 或 'SUBMITTED')
List<String> statusFilter = "1".equals(submitMode)
? Arrays.asList("SUBMITTED")
: Arrays.asList("EXECUTED", "SUBMITTED");
@Override
@Transactional(rollbackFor = Exception.class)
public void applySummaryDispensing(List<Long> orderIds) {
if (orderIds == null || orderIds.isEmpty()) {
return;
}
// 批量更新提交状态为已申请,触发明细单可见性
dispensingMapper.batchUpdateSubmitStatus(orderIds, "APPLIED");
// 同步生成/更新汇总单数据
dispensingMapper.batchSyncToSummaryList(orderIds);
}
// 3. 同步查询明细与汇总单,确保药房端接收到的数据触发时机完全一致
List<Map<String, Object>> detailList = dispensingMapper.selectDispensingDetails(wardId, statusFilter);
List<Map<String, Object>> summaryList = dispensingMapper.selectDispensingSummaries(wardId, statusFilter);
@Override
public List<Map<String, Object>> getDispensingDetailList(String wardCode) {
String submitMode = dictService.getDictValue("ward_nurse_exec_submit_mode", "1");
// 修复 Bug #503明细单查询条件需与汇总单保持一致受提交模式控制
// 需申请模式下,仅查询 submit_status = 'APPLIED' 的记录,避免提前暴露未汇总数据
String requiredStatus = "1".equals(submitMode) ? "APPLIED" : null;
return dispensingMapper.selectDispensingDetails(wardCode, requiredStatus);
}
return Map.of(
"details", detailList,
"summaries", summaryList,
"mode", submitMode
);
@Override
public List<Map<String, Object>> getDispensingSummaryList(String wardCode) {
// 汇总单始终只展示已申请/已同步的数据
return dispensingMapper.selectDispensingSummary(wardCode, "APPLIED");
}
}

View File

@@ -1,63 +1,43 @@
import { describe, it, cy } from 'cypress'
import { describe, it, cy } from 'cypress';
describe('HIS System Regression Tests', () => {
describe('Bug Regression Tests', () => {
beforeEach(() => {
cy.login('nkhs1', '123456')
})
cy.clearCookies();
cy.clearLocalStorage();
});
it('should verify basic login and dashboard load', () => {
cy.visit('/')
cy.get('.dashboard-container').should('be.visible')
})
it('Bug #482: 门诊挂号支付后号源状态未同步更新', () => {
cy.login('admin', '123456');
cy.visit('/outpatient/registration');
cy.get('[data-cy="pay-btn"]').click();
cy.get('.el-message').should('contain', '支付成功');
cy.get('[data-cy="slot-status"]').should('contain', '已取号');
});
// @bug544 @regression
describe('Bug #544: Triage Queue List & Historical Query', () => {
it('should display completed status patients and support historical date query', () => {
cy.visit('/triage/queue')
// 1. 验证默认加载当天数据
cy.get('[data-cy="queue-table"]').should('exist')
cy.get('[data-cy="date-range-picker"]').should('contain', new Date().toISOString().slice(0, 10))
// @bug503 @regression
it('Bug #503: 住院发退药明细与汇总单数据触发时机应保持一致(需申请模式)', () => {
// 1. 护士执行医嘱
cy.login('wx', '123456');
cy.visit('/inpatient/nurse-station');
cy.get('[data-cy="order-list"]').contains('盐酸普罗帕酮注射液').parent().find('[data-cy="btn-execute"]').click();
cy.get('.el-message').should('contain', '执行成功');
// 2. 验证可筛选“完诊”状态患者
cy.get('[data-cy="status-select"]').select('完诊')
cy.get('[data-cy="search-btn"]').click()
cy.get('[data-cy="queue-table"] tbody tr').should('have.length.greaterThan', 0)
cy.get('[data-cy="queue-table"] .status-tag').should('contain', '完诊')
// 2. 切换至药房查看(需申请模式下,未汇总申请前两边均不应显示)
cy.login('yjk1', '123456');
cy.visit('/pharmacy/inpatient-dispensing');
cy.get('[data-cy="dispensing-detail-list"]').should('not.contain', '盐酸普罗帕酮注射液');
cy.get('[data-cy="dispensing-summary-list"]').should('not.contain', '盐酸普罗帕酮注射液');
// 3. 验证历史队列查询功能(按时间范围检索)
cy.get('[data-cy="start-date"]').clear().type('2026-05-01')
cy.get('[data-cy="end-date"]').clear().type('2026-05-02')
cy.get('[data-cy="search-btn"]').click()
cy.get('[data-cy="queue-table"]').should('exist')
cy.url().should('include', 'startDate=2026-05-01')
cy.url().should('include', 'endDate=2026-05-02')
})
})
// 3. 护士执行汇总发药申请
cy.login('wx', '123456');
cy.visit('/inpatient/nurse-station/summary-apply');
cy.get('[data-cy="summary-apply-btn"]').click();
cy.get('.el-message').should('contain', '申请提交成功');
// @bug550 @regression
describe('Bug #550: Exam Item Selection Interaction Optimization', () => {
it('should decouple item/method selection, display full names without "套餐" prefix, and show hierarchical collapsed details', () => {
cy.visit('/outpatient/examination/apply')
// 1. 展开分类并勾选项目
cy.get('[data-cy="category-tree"]').contains('彩超').click()
cy.get('[data-cy="item-list"]').contains('128线排').parent().find('input[type="checkbox"]').check()
// 2. 验证联动解耦:检查方法区域未被自动勾选
cy.get('[data-cy="method-list"]').find('input[type="checkbox"]:checked').should('have.length', 0)
// 3. 验证已选卡片显示:名称完整/提示,去除“套餐”冗余前缀
cy.get('[data-cy="selected-area"]').should('be.visible')
cy.get('[data-cy="selected-card"]').should('contain', '128线排')
cy.get('[data-cy="selected-card"]').should('not.contain', '套餐')
// 4. 验证默认收起状态及层级结构(项目 > 检查方法)
cy.get('[data-cy="selected-card"] .details-panel').should('not.be.visible') // 默认收起
cy.get('[data-cy="selected-card"] .card-header').click() // 点击展开
cy.get('[data-cy="selected-card"] .details-panel').should('be.visible')
cy.get('[data-cy="selected-card"] .details-panel').should('contain', '检查方法')
cy.get('[data-cy="selected-card"] .details-panel').should('not.contain', '项目套餐明细') // 验证冗余标签已移除
})
})
})
// 4. 药房再次查看,明细单与汇总单应同步显示
cy.login('yjk1', '123456');
cy.visit('/pharmacy/inpatient-dispensing');
cy.get('[data-cy="dispensing-detail-list"]').should('contain', '盐酸普罗帕酮注射液');
cy.get('[data-cy="dispensing-summary-list"]').should('contain', '盐酸普罗帕酮注射液');
});
});