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:
@@ -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(绝对)
|
||||
**适用范围**: 全项目
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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强制通过)';
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
11
healthlink-his-ui/src/views/outpatientenhanced/api.js
Normal file
11
healthlink-his-ui/src/views/outpatientenhanced/api.js
Normal 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'})}
|
||||
104
healthlink-his-ui/src/views/outpatientenhanced/index.vue
Normal file
104
healthlink-his-ui/src/views/outpatientenhanced/index.vue
Normal 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>
|
||||
Reference in New Issue
Block a user