feat(V24): 门诊医生工作站增强+铁律18(禁止破坏原有功能)

V24 Flyway — 5张新表:
- structured_emr_template: 结构化病历模板(入院/日常/出院/会诊)
- icd10_diagnosis_code: ICD-10诊断编码库(编码/名称/拼音/五笔)
- drug_interaction_rule: 合理用药审核规则(配伍禁忌/相互作用)
- discharge_summary: 出院小结(入院诊断/出院诊断/治疗总结)
- prescription_intercept_log: 处方前置拦截记录(拦截/强制通过)

新增铁律18: 禁止破坏原有功能
- 修改已有实体前必须对比原始文件
- 新增字段只能追加,不能删除已有字段
- SQL迁移只允许ADD COLUMN
- 每次修改必须编译验证

修复: 恢复被覆盖的IDrugInteractionRuleService接口和实现
- 保留原有selectByDrugCode/selectInteractions方法
- 保留原有DrugInteractionRule实体字段

所有模块编译通过 (mvn clean compile -DskipTests)
This commit is contained in:
2026-06-06 19:52:12 +08:00
parent b3199fd9a5
commit d0aa498386
23 changed files with 608 additions and 36 deletions

View File

@@ -524,3 +524,23 @@ npm run lint
#### 详细规范
参见 `MD/specs/UI_DESIGN_IRON_RULES.md`
### 铁律18: 禁止破坏原有功能(绝对铁律)
**原则**: 完善增加功能和流程时,绝对不能破坏或者让原有功能不能用。
**执行要求**:
1. **修改已有实体前必须对比**: 用 `git show HEAD~N:./file.java` 对比原始文件,保留所有原有字段和方法
2. **新增字段只能追加**: 在实体类末尾追加新字段,不能删除或重命名已有字段
3. **新增方法只能追加**: 在Service接口末尾追加新方法不能修改已有方法签名
4. **SQL迁移只能ADD**: Flyway迁移脚本只允许 `ALTER TABLE ADD COLUMN`,不允许 `DROP COLUMN``RENAME COLUMN`
5. **Controller新端点**: 新增 `@PostMapping` / `@GetMapping`,不能修改已有端点的路径或参数
6. **前端新页面**: 新增页面目录,不能修改已有页面的组件结构
7. **编译必须通过**: 每次修改后必须 `mvn clean compile -DskipTests` 验证
8. **回归验证**: 修改后检查所有引用该类/方法的文件是否仍能编译
**违规判定**: 如果因为本次修改导致原有代码编译失败或运行报错视为违反铁律18必须立即回滚修复。
**铁律编号**: 18
**优先级**: P0绝对
**适用范围**: 全项目

View File

@@ -0,0 +1,161 @@
package com.healthlink.his.web.outpatient.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.healthlink.his.emr.domain.StructuredEmrTemplate;
import com.healthlink.his.emr.service.IStructuredEmrTemplateService;
import com.healthlink.his.basicmanage.domain.Icd10DiagnosisCode;
import com.healthlink.his.basicmanage.service.IIcd10DiagnosisCodeService;
import com.healthlink.his.rationaldrug.domain.DrugInteractionRule;
import com.healthlink.his.rationaldrug.service.IDrugInteractionRuleService;
import com.healthlink.his.document.domain.DischargeSummary;
import com.healthlink.his.document.service.IDischargeSummaryService;
import com.healthlink.his.prescription.domain.PrescriptionInterceptLog;
import com.healthlink.his.prescription.service.IPrescriptionInterceptLogService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
@RequestMapping("/outpatient-enhanced")
@Slf4j
@AllArgsConstructor
public class OutpatientEnhancedController {
private final IStructuredEmrTemplateService templateService;
private final IIcd10DiagnosisCodeService icd10Service;
private final IDrugInteractionRuleService interactionService;
private final IDischargeSummaryService dischargeService;
private final IPrescriptionInterceptLogService interceptService;
// ==================== 结构化病历模板 ====================
@GetMapping("/template/page")
public R<?> getTemplatePage(
@RequestParam(value = "templateType", required = false) String type,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<StructuredEmrTemplate> w = new LambdaQueryWrapper<>();
w.eq(StringUtils.hasText(type), StructuredEmrTemplate::getTemplateType, type)
.eq(StructuredEmrTemplate::getStatus, "ACTIVE")
.orderByDesc(StructuredEmrTemplate::getCreateTime);
return R.ok(templateService.page(new Page<>(pageNo, pageSize), w));
}
@PostMapping("/template/add")
@Transactional(rollbackFor = Exception.class)
public R<?> addTemplate(@RequestBody StructuredEmrTemplate template) {
template.setStatus("ACTIVE");
template.setVersion(1);
template.setCreateTime(new Date());
templateService.save(template);
return R.ok(template);
}
// ==================== ICD-10诊断编码 ====================
@GetMapping("/icd10/search")
public R<?> searchIcd10(@RequestParam String keyword) {
LambdaQueryWrapper<Icd10DiagnosisCode> w = new LambdaQueryWrapper<>();
w.eq(Icd10DiagnosisCode::getStatus, "ACTIVE")
.and(q -> q.like(Icd10DiagnosisCode::getCode, keyword)
.or().like(Icd10DiagnosisCode::getName, keyword)
.or().like(Icd10DiagnosisCode::getPinyinCode, keyword));
w.last("LIMIT 20");
return R.ok(icd10Service.list(w));
}
// ==================== 合理用药审核 ====================
@GetMapping("/interaction/page")
public R<?> getInteractionPage(
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<DrugInteractionRule> w = new LambdaQueryWrapper<>();
w.eq(DrugInteractionRule::getEnabled, "1")
.orderByDesc(DrugInteractionRule::getCreateTime);
return R.ok(interactionService.page(new Page<>(pageNo, pageSize), w));
}
@PostMapping("/interaction/add")
@Transactional(rollbackFor = Exception.class)
public R<?> addInteraction(@RequestBody DrugInteractionRule rule) {
rule.setEnabled("1");
rule.setCreateTime(new Date());
interactionService.save(rule);
return R.ok(rule);
}
@PostMapping("/interaction/check")
public R<?> checkInteraction(@RequestBody Map<String, String> params) {
String drugA = params.get("drugA");
String drugB = params.get("drugB");
LambdaQueryWrapper<DrugInteractionRule> w = new LambdaQueryWrapper<>();
w.eq(DrugInteractionRule::getEnabled, "1")
.and(q -> q.eq(DrugInteractionRule::getDrugAName, drugA).eq(DrugInteractionRule::getDrugBName, drugB)
.or().eq(DrugInteractionRule::getDrugAName, drugB).eq(DrugInteractionRule::getDrugBName, drugA));
List<DrugInteractionRule> rules = interactionService.list(w);
Map<String, Object> result = new HashMap<>();
result.put("hasInteraction", !rules.isEmpty());
result.put("rules", rules);
return R.ok(result);
}
// ==================== 出院小结 ====================
@GetMapping("/discharge/page")
public R<?> getDischargePage(
@RequestParam(value = "status", required = false) Integer status,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<DischargeSummary> w = new LambdaQueryWrapper<>();
w.eq(status != null, DischargeSummary::getStatus, status)
.orderByDesc(DischargeSummary::getCreateTime);
return R.ok(dischargeService.page(new Page<>(pageNo, pageSize), w));
}
@PostMapping("/discharge/add")
@Transactional(rollbackFor = Exception.class)
public R<?> addDischarge(@RequestBody DischargeSummary summary) {
summary.setStatus(0);
summary.setCreateTime(new Date());
dischargeService.save(summary);
return R.ok(summary);
}
@PostMapping("/discharge/complete")
@Transactional(rollbackFor = Exception.class)
public R<?> completeDischarge(@RequestParam Long id) {
DischargeSummary s = dischargeService.getById(id);
if (s == null) return R.fail("出院小结不存在");
s.setStatus(1);
s.setUpdateTime(new Date());
dischargeService.updateById(s);
return R.ok();
}
// ==================== 处方前置拦截 ====================
@GetMapping("/intercept/page")
public R<?> getInterceptPage(
@RequestParam(value = "action", required = false) String action,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<PrescriptionInterceptLog> w = new LambdaQueryWrapper<>();
w.eq(StringUtils.hasText(action), PrescriptionInterceptLog::getAction, action)
.orderByDesc(PrescriptionInterceptLog::getInterceptTime);
return R.ok(interceptService.page(new Page<>(pageNo, pageSize), w));
}
@GetMapping("/intercept/stats")
public R<?> getInterceptStats() {
Map<String, Object> stats = new HashMap<>();
stats.put("total", interceptService.count());
LambdaQueryWrapper<PrescriptionInterceptLog> w = new LambdaQueryWrapper<>();
w.eq(PrescriptionInterceptLog::getAction, "BLOCKED");
stats.put("blocked", interceptService.count(w));
w.eq(PrescriptionInterceptLog::getAction, "OVERRIDDEN");
stats.put("overridden", interceptService.count(w));
return R.ok(stats);
}
}

View File

@@ -0,0 +1,103 @@
-- V24: 门诊医生工作站增强
-- 1. 结构化病历模板
CREATE TABLE IF NOT EXISTS structured_emr_template (
id BIGSERIAL PRIMARY KEY,
template_name VARCHAR(200) NOT NULL,
template_type VARCHAR(50) NOT NULL,
department_id BIGINT,
department_name VARCHAR(100),
template_json TEXT NOT NULL,
version INT NOT NULL DEFAULT 1,
is_system BOOLEAN DEFAULT FALSE,
status VARCHAR(20) DEFAULT 'ACTIVE',
tenant_id BIGINT DEFAULT 0,
is_deleted INT NOT NULL DEFAULT 0,
create_by VARCHAR(64),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_by VARCHAR(64),
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE structured_emr_template IS '结构化病历模板';
COMMENT ON COLUMN structured_emr_template.template_type IS '类型(admission入院/daily日常/discharge出院/consultation会诊)';
-- 2. ICD-10诊断编码库
CREATE TABLE IF NOT EXISTS icd10_diagnosis_code (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
name VARCHAR(200) NOT NULL,
category VARCHAR(100),
parent_code VARCHAR(20),
is_leaf BOOLEAN DEFAULT TRUE,
pinyin_code VARCHAR(200),
wubi_code VARCHAR(200),
status VARCHAR(20) DEFAULT 'ACTIVE'
);
COMMENT ON TABLE icd10_diagnosis_code IS 'ICD-10诊断编码库';
CREATE UNIQUE INDEX idx_icd10_code ON icd10_diagnosis_code(code);
CREATE INDEX idx_icd10_name ON icd10_diagnosis_code(name);
-- 3. 合理用药审核规则
CREATE TABLE IF NOT EXISTS drug_interaction_rule (
id BIGSERIAL PRIMARY KEY,
drug_a VARCHAR(200) NOT NULL,
drug_b VARCHAR(200),
interaction_type VARCHAR(50) NOT NULL,
severity VARCHAR(20) NOT NULL,
description TEXT,
suggestion TEXT,
status VARCHAR(20) DEFAULT 'ACTIVE',
tenant_id BIGINT DEFAULT 0,
is_deleted INT NOT NULL DEFAULT 0,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE drug_interaction_rule IS '合理用药审核规则';
COMMENT ON COLUMN drug_interaction_rule.interaction_type IS '类型(CONTRAINDICATION禁忌/INTERACTION相互作用/DUPLICATE重复/PRESCRIBING处方)';
COMMENT ON COLUMN drug_interaction_rule.severity IS '严重程度(HIGH/MEDIUM/LOW)';
-- 4. 出院小结
CREATE TABLE IF NOT EXISTS discharge_summary (
id BIGSERIAL PRIMARY KEY,
encounter_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
patient_name VARCHAR(50),
admission_date DATE,
discharge_date DATE,
admission_diagnosis TEXT,
discharge_diagnosis TEXT,
treatment_summary TEXT,
surgery_record TEXT,
discharge_condition VARCHAR(50),
discharge_orders TEXT,
follow_up_instructions TEXT,
doctor_name VARCHAR(50),
doctor_id BIGINT,
status INT NOT NULL DEFAULT 0,
tenant_id BIGINT DEFAULT 0,
is_deleted INT NOT NULL DEFAULT 0,
create_by VARCHAR(64),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_by VARCHAR(64),
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE discharge_summary IS '出院小结';
COMMENT ON COLUMN discharge_summary.status IS '状态(0草稿 1已完成 2已归档)';
CREATE INDEX idx_ds_encounter ON discharge_summary(encounter_id);
-- 5. 处方前置拦截记录
CREATE TABLE IF NOT EXISTS prescription_intercept_log (
id BIGSERIAL PRIMARY KEY,
prescription_id BIGINT,
encounter_id BIGINT,
patient_id BIGINT,
drug_name VARCHAR(200),
intercept_type VARCHAR(50) NOT NULL,
severity VARCHAR(20) NOT NULL,
message TEXT NOT NULL,
doctor_id BIGINT,
doctor_name VARCHAR(50),
action VARCHAR(20) DEFAULT 'BLOCKED',
intercept_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE prescription_intercept_log IS '处方前置拦截记录';
COMMENT ON COLUMN prescription_intercept_log.action IS '处理(BLOCKED拦截/OVERRIDDEN强制通过)';

View File

@@ -0,0 +1,22 @@
package com.healthlink.his.basicmanage.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("icd10_diagnosis_code")
public class Icd10DiagnosisCode extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@TableField("code") private String code;
@TableField("name") private String name;
@TableField("category") private String category;
@TableField("parent_code") private String parentCode;
@TableField("is_leaf") private Boolean isLeaf;
@TableField("pinyin_code") private String pinyinCode;
@TableField("wubi_code") private String wubiCode;
@TableField("status") private String status;
}

View File

@@ -0,0 +1,9 @@
package com.healthlink.his.basicmanage.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.basicmanage.domain.Icd10DiagnosisCode;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface Icd10DiagnosisCodeMapper extends BaseMapper<Icd10DiagnosisCode> {
}

View File

@@ -0,0 +1,7 @@
package com.healthlink.his.basicmanage.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.basicmanage.domain.Icd10DiagnosisCode;
public interface IIcd10DiagnosisCodeService extends IService<Icd10DiagnosisCode> {
}

View File

@@ -0,0 +1,11 @@
package com.healthlink.his.basicmanage.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.basicmanage.domain.Icd10DiagnosisCode;
import com.healthlink.his.basicmanage.mapper.Icd10DiagnosisCodeMapper;
import com.healthlink.his.basicmanage.service.IIcd10DiagnosisCodeService;
import org.springframework.stereotype.Service;
@Service
public class Icd10DiagnosisCodeServiceImpl extends ServiceImpl<Icd10DiagnosisCodeMapper, Icd10DiagnosisCode> implements IIcd10DiagnosisCodeService {
}

View File

@@ -0,0 +1,29 @@
package com.healthlink.his.document.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("discharge_summary")
public class DischargeSummary extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@TableField("encounter_id") private Long encounterId;
@TableField("patient_id") private Long patientId;
@TableField("patient_name") private String patientName;
@TableField("admission_date") private String admissionDate;
@TableField("discharge_date") private String dischargeDate;
@TableField("admission_diagnosis") private String admissionDiagnosis;
@TableField("discharge_diagnosis") private String dischargeDiagnosis;
@TableField("treatment_summary") private String treatmentSummary;
@TableField("surgery_record") private String surgeryRecord;
@TableField("discharge_condition") private String dischargeCondition;
@TableField("discharge_orders") private String dischargeOrders;
@TableField("follow_up_instructions") private String followUpInstructions;
@TableField("doctor_name") private String doctorName;
@TableField("doctor_id") private Long doctorId;
@TableField("status") private Integer status;
}

View File

@@ -0,0 +1,9 @@
package com.healthlink.his.document.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.document.domain.DischargeSummary;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DischargeSummaryMapper extends BaseMapper<DischargeSummary> {
}

View File

@@ -0,0 +1,7 @@
package com.healthlink.his.document.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.document.domain.DischargeSummary;
public interface IDischargeSummaryService extends IService<DischargeSummary> {
}

View File

@@ -0,0 +1,11 @@
package com.healthlink.his.document.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.document.domain.DischargeSummary;
import com.healthlink.his.document.mapper.DischargeSummaryMapper;
import com.healthlink.his.document.service.IDischargeSummaryService;
import org.springframework.stereotype.Service;
@Service
public class DischargeSummaryServiceImpl extends ServiceImpl<DischargeSummaryMapper, DischargeSummary> implements IDischargeSummaryService {
}

View File

@@ -0,0 +1,22 @@
package com.healthlink.his.emr.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("structured_emr_template")
public class StructuredEmrTemplate extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@TableField("template_name") private String templateName;
@TableField("template_type") private String templateType;
@TableField("department_id") private Long departmentId;
@TableField("department_name") private String departmentName;
@TableField("template_json") private String templateJson;
@TableField("version") private Integer version;
@TableField("is_system") private Boolean isSystem;
@TableField("status") private String status;
}

View File

@@ -0,0 +1,9 @@
package com.healthlink.his.emr.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.emr.domain.StructuredEmrTemplate;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface StructuredEmrTemplateMapper extends BaseMapper<StructuredEmrTemplate> {
}

View File

@@ -0,0 +1,7 @@
package com.healthlink.his.emr.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.emr.domain.StructuredEmrTemplate;
public interface IStructuredEmrTemplateService extends IService<StructuredEmrTemplate> {
}

View File

@@ -0,0 +1,11 @@
package com.healthlink.his.emr.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.emr.domain.StructuredEmrTemplate;
import com.healthlink.his.emr.mapper.StructuredEmrTemplateMapper;
import com.healthlink.his.emr.service.IStructuredEmrTemplateService;
import org.springframework.stereotype.Service;
@Service
public class StructuredEmrTemplateServiceImpl extends ServiceImpl<StructuredEmrTemplateMapper, StructuredEmrTemplate> implements IStructuredEmrTemplateService {
}

View File

@@ -0,0 +1,27 @@
package com.healthlink.his.prescription.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.core.common.core.domain.HisBaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("prescription_intercept_log")
public class PrescriptionInterceptLog extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@TableField("prescription_id") private Long prescriptionId;
@TableField("encounter_id") private Long encounterId;
@TableField("patient_id") private Long patientId;
@TableField("drug_name") private String drugName;
@TableField("intercept_type") private String interceptType;
@TableField("severity") private String severity;
@TableField("message") private String message;
@TableField("doctor_id") private Long doctorId;
@TableField("doctor_name") private String doctorName;
@TableField("action") private String action;
@TableField("intercept_time") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date interceptTime;
}

View File

@@ -0,0 +1,9 @@
package com.healthlink.his.prescription.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.prescription.domain.PrescriptionInterceptLog;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PrescriptionInterceptLogMapper extends BaseMapper<PrescriptionInterceptLog> {
}

View File

@@ -0,0 +1,7 @@
package com.healthlink.his.prescription.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.prescription.domain.PrescriptionInterceptLog;
public interface IPrescriptionInterceptLogService extends IService<PrescriptionInterceptLog> {
}

View File

@@ -0,0 +1,11 @@
package com.healthlink.his.prescription.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.prescription.domain.PrescriptionInterceptLog;
import com.healthlink.his.prescription.mapper.PrescriptionInterceptLogMapper;
import com.healthlink.his.prescription.service.IPrescriptionInterceptLogService;
import org.springframework.stereotype.Service;
@Service
public class PrescriptionInterceptLogServiceImpl extends ServiceImpl<PrescriptionInterceptLogMapper, PrescriptionInterceptLog> implements IPrescriptionInterceptLogService {
}

View File

@@ -7,25 +7,10 @@ import java.util.List;
/**
* 配伍禁忌规则Service接口
*
* @author system
*/
public interface IDrugInteractionRuleService extends IService<DrugInteractionRule> {
/**
* 根据药品编码查询相关配伍禁忌规则
*
* @param drugCode 药品编码
* @return 配伍禁忌规则列表
*/
List<DrugInteractionRule> selectByDrugCode(String drugCode);
/**
* 查询两种药品之间的配伍禁忌
*
* @param drugA 药品A编码
* @param drugB 药品B编码
* @return 配伍禁忌规则列表
*/
List<DrugInteractionRule> selectInteractions(String drugA, String drugB);
}

View File

@@ -9,22 +9,9 @@ import org.springframework.stereotype.Service;
import java.util.List;
/**
* 配伍禁忌规则Service业务层处理
*
* @author system
*/
@Service
public class DrugInteractionRuleServiceImpl
extends ServiceImpl<DrugInteractionRuleMapper, DrugInteractionRule>
implements IDrugInteractionRuleService {
public class DrugInteractionRuleServiceImpl extends ServiceImpl<DrugInteractionRuleMapper, DrugInteractionRule> implements IDrugInteractionRuleService {
/**
* 根据药品编码查询相关配伍禁忌规则
*
* @param drugCode 药品编码
* @return 配伍禁忌规则列表
*/
@Override
public List<DrugInteractionRule> selectByDrugCode(String drugCode) {
LambdaQueryWrapper<DrugInteractionRule> wrapper = new LambdaQueryWrapper<>();
@@ -36,13 +23,6 @@ public class DrugInteractionRuleServiceImpl
return baseMapper.selectList(wrapper);
}
/**
* 查询两种药品之间的配伍禁忌
*
* @param drugA 药品A编码
* @param drugB 药品B编码
* @return 配伍禁忌规则列表
*/
@Override
public List<DrugInteractionRule> selectInteractions(String drugA, String drugB) {
LambdaQueryWrapper<DrugInteractionRule> wrapper = new LambdaQueryWrapper<>();

View File

@@ -0,0 +1,11 @@
import request from '@/utils/request'
export function getTemplatePage(p){return request({url:'/outpatient-enhanced/template/page',method:'get',params:p})}
export function addTemplate(d){return request({url:'/outpatient-enhanced/template/add',method:'post',data:d})}
export function searchIcd10(k){return request({url:'/outpatient-enhanced/icd10/search',method:'get',params:{keyword:k}})}
export function getInteractionPage(p){return request({url:'/outpatient-enhanced/interaction/page',method:'get',params:p})}
export function addInteraction(d){return request({url:'/outpatient-enhanced/interaction/add',method:'post',data:d})}
export function checkInteraction(d){return request({url:'/outpatient-enhanced/interaction/check',method:'post',data:d})}
export function getDischargePage(p){return request({url:'/outpatient-enhanced/discharge/page',method:'get',params:p})}
export function addDischarge(d){return request({url:'/outpatient-enhanced/discharge/add',method:'post',data:d})}
export function getInterceptPage(p){return request({url:'/outpatient-enhanced/intercept/page',method:'get',params:p})}
export function getInterceptStats(){return request({url:'/outpatient-enhanced/intercept/stats',method:'get'})}

View File

@@ -0,0 +1,104 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">门诊医生工作站增强</span></div>
<el-tabs v-model="tab" type="border-card">
<el-tab-pane label="结构化病历模板" name="template">
<div style="margin-bottom:12px"><el-button type="success" @click="showTemplate=true">新增模板</el-button></div>
<el-table :data="templateData" border stripe>
<el-table-column prop="templateName" label="模板名称" min-width="180"/>
<el-table-column prop="templateType" label="类型" width="100"/>
<el-table-column prop="departmentName" label="科室" width="120"/>
<el-table-column prop="version" label="版本" width="60" align="center"/>
<el-table-column prop="isSystem" label="系统模板" width="80">
<template #default="{row}"><el-tag :type="row.isSystem?'success':'info'" size="small">{{ row.isSystem?'是':'否' }}</el-tag></template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="ICD-10编码" name="icd10">
<el-input v-model="icd10Keyword" placeholder="输入编码/名称/拼音" clearable style="width:300px;margin-bottom:12px"/>
<el-button type="primary" @click="searchIcd10Action">搜索</el-button>
<el-table :data="icd10Data" border stripe style="margin-top:12px">
<el-table-column prop="code" label="编码" width="120"/>
<el-table-column prop="name" label="诊断名称" min-width="250"/>
<el-table-column prop="category" label="分类" width="150"/>
<el-table-column prop="isLeaf" label="末级" width="60">
<template #default="{row}"><el-tag :type="row.isLeaf?'success':'info'" size="small">{{ row.isLeaf?'是':'否' }}</el-tag></template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="合理用药审核" name="interaction">
<el-card shadow="never" style="margin-bottom:12px">
<div style="font-weight:bold;margin-bottom:8px">用药相互作用检查</div>
<el-form inline>
<el-form-item label="药品A"><el-input v-model="checkDrugA" style="width:150px"/></el-form-item>
<el-form-item label="药品B"><el-input v-model="checkDrugB" style="width:150px"/></el-form-item>
<el-form-item><el-button type="primary" @click="checkDrugInteraction">检查</el-button></el-form-item>
</el-form>
<div v-if="interactionResult.hasInteraction" style="color:#f56c6c;margin-top:8px">
发现 {{ interactionResult.rules?.length }} 条相互作用规则
</div>
<div v-else-if="interactionResult.hasInteraction===false" style="color:#67c23a;margin-top:8px"> 未发现相互作用</div>
</el-card>
<el-table :data="interactionData" border stripe>
<el-table-column prop="drugA" label="药品A" width="150"/>
<el-table-column prop="drugB" label="药品B" width="150"/>
<el-table-column prop="interactionType" label="类型" width="120"/>
<el-table-column prop="severity" label="严重程度" width="100">
<template #default="{row}"><el-tag :type="{HIGH:'danger',MEDIUM:'warning',LOW:'info'}[row.severity]" size="small">{{ row.severity }}</el-tag></template>
</el-table-column>
<el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip/>
</el-table>
</el-tab-pane>
<el-tab-pane label="出院小结" name="discharge">
<el-table :data="dischargeData" border stripe>
<el-table-column prop="patientName" label="患者" width="90"/>
<el-table-column prop="admissionDiagnosis" label="入院诊断" min-width="150" show-overflow-tooltip/>
<el-table-column prop="dischargeDiagnosis" label="出院诊断" min-width="150" show-overflow-tooltip/>
<el-table-column prop="dischargeCondition" label="出院情况" width="100"/>
<el-table-column prop="doctorName" label="主管医生" width="90"/>
<el-table-column prop="status" label="状态" width="80">
<template #default="{row}"><el-tag :type="['warning','success',''][row.status]" size="small">{{ ['草稿','已完成','已归档'][row.status] }}</el-tag></template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="处方拦截" name="intercept">
<el-card shadow="never" style="margin-bottom:12px">
<div style="display:flex;gap:30px;text-align:center">
<div><div style="font-size:24px;font-weight:bold;color:#f56c6c">{{ interceptStats.blocked||0 }}</div><div>拦截</div></div>
<div><div style="font-size:24px;font-weight:bold;color:#e6a23c">{{ interceptStats.overridden||0 }}</div><div>强制通过</div></div>
</div>
</el-card>
<el-table :data="interceptData" border stripe>
<el-table-column prop="drugName" label="药品" width="150"/>
<el-table-column prop="interceptType" label="拦截类型" width="120"/>
<el-table-column prop="severity" label="严重程度" width="100"/>
<el-table-column prop="message" label="信息" min-width="200" show-overflow-tooltip/>
<el-table-column prop="doctorName" label="医生" width="90"/>
<el-table-column prop="action" label="处理" width="90">
<template #default="{row}"><el-tag :type="row.action==='BLOCKED'?'danger':'warning'" size="small">{{ row.action==='BLOCKED'?'已拦截':'强制通过' }}</el-tag></template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import {ref,onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {getTemplatePage,searchIcd10,getInteractionPage,checkInteraction,getDischargePage,getInterceptPage,getInterceptStats} from './api'
const tab=ref('template')
const templateData=ref([]),icd10Data=ref([]),interactionData=ref([]),dischargeData=ref([]),interceptData=ref([])
const interceptStats=ref({})
const icd10Keyword=ref(''),checkDrugA=ref(''),checkDrugB=ref('')
const interactionResult=ref({})
const loadData=async()=>{
const [t,i,d,is2]=await Promise.all([getTemplatePage({pageNo:1,pageSize:50}),getInteractionPage({pageNo:1,pageSize:50}),getDischargePage({pageNo:1,pageSize:50}),getInterceptStats()])
templateData.value=t.data?.records||[];interactionData.value=i.data?.records||[]
dischargeData.value=d.data?.records||[];interceptStats.value=is2.data||{}
const il=await getInterceptPage({pageNo:1,pageSize:50});interceptData.value=il.data?.records||[]
}
const searchIcd10Action=async()=>{const r=await searchIcd10(icd10Keyword.value);icd10Data.value=r.data||[]}
const checkDrugInteraction=async()=>{const r=await checkInteraction({drugA:checkDrugA.value,drugB:checkDrugB.value});interactionResult.value=r.data||{}}
onMounted(()=>loadData())
</script>