基于ASP.NET Core的医院不良事件管理系统的架构设计
前言
医院不良事件管理是医疗质量安全管理的重要环节,一个优秀的不良事件管理系统需要覆盖事件上报、审核追踪、分析统计的完整闭环。本文将详细介绍如何基于ASP.NET Core构建一个完整的医院不良事件管理系统,涵盖26种不良事件类型、完善的权限体系、钉钉集成以及数据可视化统计。
一、系统整体架构
1.1 技术栈选型
csharp
// 核心技术栈
- 框架: ASP.NET Core 3.1+
- 前端UI: Layui 2.9.7
- 数据库: MySQL (主数据库)
- 辅助数据库: Oracle (人事系统), SQL Server (HIS), PostgreSQL (LIS)
- ORM: Dapper (轻量级高性能)
- 图表: ECharts 5.x
- 即时通讯: 钉钉SDK
- PDF生成: QuestPDF, iText
- Excel操作: NPOI
1.2 项目结构
QASystem/
├── Controllers/ # 控制器层
│ ├── LoginController.cs # 登录认证
│ ├── HarmfulEventController.cs # 不良事件上报
│ ├── HarmfulEventManageController.cs # 不良事件管理
│ └── HarmfulChartController.cs # 图表统计
├── Models/ # 数据模型
│ ├── Login/ # 登录相关模型
│ ├── HarmfulEvent/ # 不良事件模型
│ ├── HarmfulEventManage/ # 管理模块模型
│ └── HarmfulChart/ # 图表模型
├── Views/ # 视图层
│ ├── Login/ # 登录视图
│ ├── HarmfulEvent/ # 26种事件表单视图
│ ├── HarmfulEventManage/ # 管理视图
│ └── HarmfulChart/ # 图表视图
├── Services/ # 业务服务层
├── Unitity/ # 工具类
└── Configuration/ # 配置文件
1.3 多数据库架构
系统需要对接多个业务系统,采用了多数据库连接架构:
csharp
public class HarmfulEventController : CheckLoginController
{
private readonly string _harmfulevent; // 不良事件数据库
private readonly string _qasystem; // QA系统数据库
private readonly string _his3; // HIS3数据库
private readonly string _sihis; // SI医院信息系统
private readonly string _lyradb; // LIS数据库
public HarmfulEventController(IConfiguration configuration)
{
_harmfulevent = configuration.GetConnectionString("harmfulevent");
_qasystem = configuration.GetConnectionString("qasystem");
_his3 = configuration.GetConnectionString("his3");
_sihis = configuration.GetConnectionString("sihis");
_lyradb = configuration.GetConnectionString("lyradb");
}
}
这种设计使得系统能够:
- 从HIS自动获取患者信息
- 从人事系统获取员工数据
- 从LIS获取检验数据
- 统一管理不良事件数据
二、数据库设计
2.1 核心表结构
sql
-- 不良事件主表
CREATE TABLE `medreportmt` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`repId` VARCHAR(50) NOT NULL, -- 事件编号
`repTypeId` INT NOT NULL, -- 事件类型ID
`repTypeId1` INT NOT NULL, -- 事件类型ID(可修改)
`status` TINYINT DEFAULT 0, -- 状态:0暂存/1上报/2审核
`empId` INT NOT NULL, -- 上报人ID
`empDeptId` INT NOT NULL, -- 上报科室ID
`createDate` DATETIME, -- 创建时间
`submitDate` DATETIME, -- 提交时间
`isTrack` TINYINT DEFAULT 0, -- 追踪状态:0未/1可/2中/3已
`isSend` TINYINT DEFAULT 0, -- 是否转发
`sac` INT DEFAULT 0, -- SAC分级
UNIQUE KEY `repId` (`repId`)
);
-- 不良事件明细表
CREATE TABLE `medreportdt` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`repId` VARCHAR(50) NOT NULL, -- 关联事件编号
`repContent` JSON NOT NULL, -- 事件详细内容(JSON)
FOREIGN KEY (`repId`) REFERENCES `medreportmt`(`repId`)
);
-- 事件追踪表
CREATE TABLE `medreptrack` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`repId` VARCHAR(50) NOT NULL, -- 关联事件编号
`trackcontent` JSON NOT NULL, -- 追踪内容
`auditContent` JSON, -- 审核内容
FOREIGN KEY (`repId`) REFERENCES `medreportmt`(`repId`)
);
-- 事件转发表
CREATE TABLE `medreportsd` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`repId` VARCHAR(50) NOT NULL,
`sendEmpId` INT NOT NULL, -- 发送人
`acceptEmpId` INT NOT NULL, -- 接收人
`sendDate` DATETIME
);
-- 附件表
CREATE TABLE `medreportfiles` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`repId` VARCHAR(50) NOT NULL,
`fileName` VARCHAR(255),
`fileExt` VARCHAR(20),
`fileSize` VARCHAR(50),
`filePath` VARCHAR(500),
`uploader` INT,
`uploadTime` DATETIME
);
2.2 事件内容JSON结构
不同类型的不良事件使用JSON存储灵活的事件内容:
csharp
// 以药品不良反应为例 (HarmfulEvent06Model)
public class HarmfulEvent06Model
{
public string deptid { get; set; } // 发生科室
public string occutime { get; set; } // 发生时间
public string occuaddr { get; set; } // 发生地点
public string pname { get; set; } // 患者姓名
public string psex { get; set; } // 性别
public string age { get; set; } // 年龄
public string diagnose { get; set; } // 临床诊断
public string mrn { get; set; } // 病历号
public string eventtype { get; set; } // 事件类型
public string effect { get; set; } // 损害程度
public string process { get; set; } // 发生过程
public string analysis { get; set; } // 原因分析
public string advise { get; set; } // 建议
// 药品相关字段
public string impact { get; set; } // 对原患疾病影响
public string evaluation { get; set; } // 关联性评价
public string stopdrug { get; set; } // 停药情况
public string usedagain { get; set; } // 再次使用
public string eventresult { get; set; } // 不良反应结果
}
三、控制器设计
3.1 控制器职责划分
LoginController - 认证授权、钉钉集成、SSO单点登录
│
├─> HarmfulEventController - 26种事件的上报、编辑、查看
│ ├─ HarmfulEvent01Add/Edit/Show (输血不良反应)
│ ├─ HarmfulEvent02Add/Edit/Show (输血相关事件)
│ ├─ ...
│ └─ HarmfulEvent26Add/Edit/Show (非计划再次手术)
│
├─> HarmfulEventManageController - 审核、追踪、转发、导出
│ ├─ Index (管理首页)
│ ├─ HarmfulEventManageAudit (审核)
│ ├─ HarmfulEventManageTrack (追踪)
│ ├─ HarmfulEventManageSend (转发)
│ └─ HarmfulEventManageExportExcel (导出)
│
└─> HarmfulChartController - 7种统计图表
├─ echart01 (按科室统计趋势)
├─ echart02 (按伤害程度饼图)
├─ echart03 (按事件类型统计)
├─ echart04 (按人员类别饼图)
├─ echart05 (按月份季度统计)
├─ echart06 (按SAC分级饼图)
└─ echart07 (按事件类型饼图)
3.2 统一响应模型
csharp
// 统一的API响应格式
public class DataTableModel<T>
{
public int code { get; set; } // 0成功/1失败
public int count { get; set; } // 总记录数
public string msg { get; set; } // 消息
public List<T> data { get; set; } // 数据
}
public class MsgModel
{
public int code { get; set; }
public string msg { get; set; }
}
3.3 分页查询实现
csharp
public IActionResult HarmfulEventManageList(HarmfulEventManageSearchModel model)
{
int offset = (model.page - 1) * model.limit;
int rows = model.limit;
List<string> whereList = new List<string>();
var countPm = new DynamicParameters();
var pagePm = new DynamicParameters();
// 动态构建查询条件
if (model.repTypeId > 0)
{
whereList.Add("repTypeId1=?repTypeId1");
countPm.Add("?repTypeId1", model.repTypeId);
pagePm.Add("?repTypeId1", model.repTypeId);
}
if (!string.IsNullOrEmpty(model.submitDate))
{
string[] dates = model.submitDate.Split('~');
whereList.Add("submitDate between ?startTime and ?endTime");
countPm.Add("?startTime", dates[0].Trim() + " 00:00:00");
countPm.Add("?endTime", dates[1].Trim() + " 23:59:59");
pagePm.Add("?startTime", dates[0].Trim() + " 00:00:00");
pagePm.Add("?endTime", dates[1].Trim() + " 23:59:59");
}
string wheresql = string.Join(" and ", whereList);
string sqlPage = sql + " where " + wheresql + " ORDER BY submitDate DESC LIMIT ?pageoff,?limit";
MedreportmtInfos = _mysqlService.DBQuery<MedreportmtModel>(_harmfulevent, sqlPage, pagePm);
MedreportmtCount = _mysqlService.DBQuery<MedreportmtModel>(_harmfulevent, sqlCount, countPm);
return Json(new DataTableModel<T> {
code = 0,
count = MedreportmtCount.Count,
data = MedreportmtInfos
});
}
四、会话与权限管理
4.1 Session存储用户信息
csharp
// 登录时设置Session
private void SetEmployeeRoles(EmployeeModel LoginUser)
{
HttpContext.Session.SetString("Employee", JsonConvert.SerializeObject(LoginUser));
HttpContext.Session.SetString("EventTypes", JsonConvert.SerializeObject(
DI.QueryMedreptypes(_mysqlService, _harmfulevent, LoginUser.roleId)));
// 权限控制
HttpContext.Session.SetString("CanTrack",
DI.isPermissions(_mysqlService, _qasystem, LoginUser.roleId, 47)); // 追踪权限
HttpContext.Session.SetString("CanTrackAudit",
DI.isPermissions(_mysqlService, _qasystem, LoginUser.roleId, 50)); // 追踪审核权限
HttpContext.Session.SetString("CanSend",
DI.isPermissions(_mysqlService, _qasystem, LoginUser.roleId, 51)); // 转发权限
HttpContext.Session.SetString("DeptManager",
DI.isPermissions(_mysqlService, _qasystem, LoginUser.roleId, 42)); // 科室管理权限
}
4.2 权限验证基类
csharp
public class CheckLoginController : Controller
{
public new IActionResult View()
{
if (string.IsNullOrEmpty(HttpContext.Session.GetString("Employee")))
{
return Redirect("/Login/UserLogin");
}
return base.View();
}
}
五、前端架构
5.1 Layui表格组件
html
<table id="HarmfulEventsManageTable" lay-filter="HarmfulEventsManageTable"></table>
<script>
layui.use(['index', 'table'], function(){
var table = layui.table;
table.render({
elem: '#HarmfulEventsManageTable'
,url: '/HarmfulEventManage/HarmfulEventManageList'
,cols: [[
{type: 'checkbox', fixed: 'left'}
,{field: 'repId', width: 160, title: '文书ID'}
,{field: 'status', width: 70, title: '状态', templet: '#status'}
,{field: 'isTrack', width: 80, title: '追踪', templet: '#isTrack'}
,{field: 'repTypeName', width: 250, title: '事件类型'}
]]
,page: true
,limit: 20
,limits: [20, 100, 200, 300]
});
});
</script>
5.2 事件类型模板
html
<script type="text/html" id="status">
{{# if(d.status == 0){ }}
<button class="layui-btn layui-btn-warm layui-btn-xs">暂存</button>
{{# }else if(d.status == 1){ }}
<button class="layui-btn layui-btn-normal layui-btn-xs">上报</button>
{{# }else if(d.status == 2){ }}
<button class="layui-btn layui-btn-xs">审核</button>
{{# } }}
</script>
六、26种不良事件类型
系统支持26种不良事件,涵盖了医院运营的各个关键环节:
| 编号 | 事件类型 | 特殊字段 |
|---|---|---|
| 01 | 输血不良反应 | 血型、配血结果 |
| 02 | 输血相关事件 | 血袋信息 |
| 03 | 手术并发症 | 手术信息、麻醉方式 |
| 04 | 术前术后诊断不符 | 诊断对比 |
| 05 | 其他手术相关事件 | 手术部位错误等 |
| 06 | 药品不良反应 | 用药信息、过敏史 |
| 07 | 用药差错事件 | 差错环节分类 |
| 08 | 职业暴露事件 | 暴露类型、病毒类型 |
| 09 | 医院感染相关 | 感染类型 |
| 10 | 麻醉镇静相关 | 麻醉方式 |
| 11 | 管路事件 | 管路类型、滑脱方式 |
| 12 | 公共意外事件 | 意外类型 |
| 13 | 跌倒坠床事件 | 跌倒评分、跌倒部位 |
| 14 | 治安事件 | 事件类型 |
| 15 | 其他不良事件 | - |
| 16 | 医疗设备器械 | 设备信息 |
| 17 | 其他医疗不良事件 | - |
| 18 | 护理给药错误 | 给药途径 |
| 19 | 其他护理不良事件 | 护理缺陷 |
| 20 | 压力性损伤 | 压疮分期 |
| 21 | 院感爆发 | 爆发类型 |
| 22 | 信息不良事件 | 网络安全 |
| 23 | 院内不预期心跳停止 | 心肺复苏 |
| 24 | 检查检验病理切片 | 标本信息 |
| 25 | 输液并发症 | 静脉炎分级 |
| 26 | 非计划再次手术 | 再手术原因 |
七、系统亮点
7.1 灵活的事件类型扩展
通过JSON存储事件内容,可以轻松添加新的事件类型而无需修改数据库结构。
7.2 多系统数据整合
通过统一的数据访问层,整合了HIS、LIS、人事系统等多个数据源。
7.3 完善的权限体系
基于角色的权限控制,细粒度的功能权限和数据权限管理。
7.4 实时的钉钉通知
关键操作自动推送钉钉消息,实现闭环管理。
总结
本文介绍了医院不良事件管理系统的整体架构设计,包括多数据库架构、26种事件类型设计、统一响应模型、Session权限管理等核心模块。下一篇文章将详细介绍权限认证与钉钉集成的具体实现。