LIS 条码打印管理系统
信息系统(LIS)条码打印管理解决方案,支持检验申请对接、智能条码生成、标签模板设计、多打印机管理、标本追踪等功能。
一、系统架构
LisBarcodePrinting/
├── LisBarcodePrinting.csproj
├── Program.cs
├── MainForm.cs
├── MainForm.Designer.cs
├── Core/
│ ├── LisInterface.cs ★ LIS系统接口
│ ├── BarcodeGenerator.cs # 条码生成核心
│ ├── LabelPrinter.cs # 标签打印管理
│ ├── TemplateEngine.cs # 模板引擎
│ └── SpecimenTracker.cs # 标本追踪
├── Forms/
│ ├── PrintDashboard.cs # 打印控制台
│ ├── TemplateDesigner.cs # 模板设计器
│ ├── PrinterManager.cs # 打印机管理
│ ├── OrderMonitor.cs # 申请单监控
│ └── SettingsForm.cs # 系统设置
├── Models/
│ ├── TestOrder.cs # 检验申请
│ ├── Specimen.cs # 标本信息
│ ├── LabelTemplate.cs # 标签模板
│ ├── PrintJob.cs # 打印任务
│ └── Patient.cs # 患者信息
├── Data/
│ ├── DatabaseManager.cs # 数据访问层
│ ├── LisDataSync.cs # LIS数据同步
│ └── CacheManager.cs # 缓存管理
├── Hardware/
│ ├── BarcodeScanner.cs # 条码扫描枪
│ ├── LabelPrinter.cs # 标签打印机
│ └── CardReader.cs # 就诊卡读卡器
└── Utils/
├── ConfigManager.cs # 配置管理
├── Logger.cs # 日志系统
└── Validator.cs # 数据验证
二、核心代码实现
1. 主程序入口 (Program.cs)
using System;
using System.Windows.Forms;
using LisBarcodePrinting.Core;
using LisBarcodePrinting.Utils;
namespace LisBarcodePrinting
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 初始化日志系统
Logger.Initialize();
// 初始化配置
ConfigManager.Initialize();
// 初始化数据库连接
DatabaseManager.Initialize();
// 启动LIS接口服务
LisInterface.Start();
Application.Run(new MainForm());
}
}
}
2. 主窗体 (MainForm.cs)
using System;
using System.Drawing;
using System.Windows.Forms;
using LisBarcodePrinting.Core;
using LisBarcodePrinting.Models;
using LisBarcodePrinting.Forms;
using LisBarcodePrinting.Data;
namespace LisBarcodePrinting
{
public partial class MainForm : Form
{
private Timer refreshTimer;
private int pendingOrdersCount = 0;
private int todayPrintCount = 0;
public MainForm()
{
InitializeComponent();
InitializeSystem();
}
private void InitializeComponent()
{
this.Text = "医院实验室 LIS 条码打印管理系统";
this.Size = new Size(1400, 900);
this.StartPosition = FormStartPosition.CenterScreen;
this.WindowState = FormWindowState.Maximized;
// 顶部状态栏
Panel statusPanel = new Panel
{
Dock = DockStyle.Top,
Height = 70,
BackColor = Color.FromArgb(41, 128, 185)
};
lblSystemTitle = new Label
{
Text = "🏥 实验室 LIS 条码打印系统",
ForeColor = Color.White,
Font = new Font("微软雅黑", 16, FontStyle.Bold),
Location = new Point(20, 15),
Size = new Size(400, 40)
};
lblPendingOrders = new Label
{
Text = "待打印申请: 0",
ForeColor = Color.Yellow,
Font = new Font("微软雅黑", 12, FontStyle.Bold),
Location = new Point(500, 20),
Size = new Size(150, 25)
};
lblTodayPrints = new Label
{
Text = "今日打印: 0",
ForeColor = Color.LightGreen,
Font = new Font("微软雅黑", 12, FontStyle.Bold),
Location = new Point(700, 20),
Size = new Size(150, 25)
};
lblServerStatus = new Label
{
Text = "LIS连接: 正常",
ForeColor = Color.LightGreen,
Font = new Font("微软雅黑", 10),
Location = new Point(1100, 20),
Size = new Size(120, 25)
};
statusPanel.Controls.Add(lblSystemTitle);
statusPanel.Controls.Add(lblPendingOrders);
statusPanel.Controls.Add(lblTodayPrints);
statusPanel.Controls.Add(lblServerStatus);
// 左侧导航菜单
Panel navPanel = new Panel
{
Dock = DockStyle.Left,
Width = 200,
BackColor = Color.FromArgb(52, 73, 94)
};
string[] menuItems = {
"打印控制台", "模板设计", "打印机管理",
"申请单监控", "标本追踪", "系统设置"
};
int yPos = 20;
foreach (string item in menuItems)
{
Button btn = new Button
{
Text = item,
Location = new Point(10, yPos),
Size = new Size(180, 45),
FlatStyle = FlatStyle.Flat,
ForeColor = Color.White,
BackColor = Color.FromArgb(52, 73, 94),
Font = new Font("微软雅黑", 10),
TextAlign = ContentAlignment.MiddleLeft,
Padding = new Padding(20, 0, 0, 0)
};
btn.FlatAppearance.BorderSize = 0;
btn.Click += (s, e) => NavigateToForm(item);
navPanel.Controls.Add(btn);
yPos += 55;
}
// 主内容区域
Panel mainPanel = new Panel
{
Dock = DockStyle.Fill,
BackColor = Color.WhiteSmoke
};
// 快速操作面板
GroupBox gbQuickActions = new GroupBox
{
Text = "快速操作",
Location = new Point(20, 20),
Size = new Size(400, 150),
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
btnPrintSelected = new Button
{
Text = "打印选中申请",
Location = new Point(20, 40),
Size = new Size(120, 40),
BackColor = Color.FromArgb(46, 204, 113),
ForeColor = Color.White,
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
btnPrintSelected.Click += BtnPrintSelected_Click;
btnReprint = new Button
{
Text = "补打条码",
Location = new Point(160, 40),
Size = new Size(120, 40),
BackColor = Color.FromArgb(52, 152, 219),
ForeColor = Color.White,
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
btnReprint.Click += BtnReprint_Click;
btnScanPrint = new Button
{
Text = "扫码打印",
Location = new Point(20, 90),
Size = new Size(120, 40),
BackColor = Color.FromArgb(155, 89, 182),
ForeColor = Color.White,
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
btnScanPrint.Click += BtnScanPrint_Click;
btnBatchPrint = new Button
{
Text = "批量打印",
Location = new Point(160, 90),
Size = new Size(120, 40),
BackColor = Color.FromArgb(231, 76, 60),
ForeColor = Color.White,
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
btnBatchPrint.Click += BtnBatchPrint_Click;
gbQuickActions.Controls.Add(btnPrintSelected);
gbQuickActions.Controls.Add(btnReprint);
gbQuickActions.Controls.Add(btnScanPrint);
gbQuickActions.Controls.Add(btnBatchPrint);
// 申请单列表
GroupBox gbOrders = new GroupBox
{
Text = "待打印检验申请",
Location = new Point(20, 190),
Size = new Size(800, 500),
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
dgvOrders = new DataGridView
{
Location = new Point(10, 25),
Size = new Size(780, 460),
AutoGenerateColumns = false,
AllowUserToAddRows = false,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
MultiSelect = true,
BackgroundColor = Color.White,
RowHeadersVisible = false
};
// 设置列
dgvOrders.Columns.Add("OrderNo", "申请单号");
dgvOrders.Columns.Add("PatientName", "患者姓名");
dgvOrders.Columns.Add("PatientId", "病历号");
dgvOrders.Columns.Add("TestType", "检验项目");
dgvOrders.Columns.Add("SampleType", "标本类型");
dgvOrders.Columns.Add("Priority", "优先级");
dgvOrders.Columns.Add("RequestTime", "申请时间");
dgvOrders.Columns.Add("Doctor", "申请医生");
dgvOrders.Columns["OrderNo"].Width = 100;
dgvOrders.Columns["PatientName"].Width = 80;
dgvOrders.Columns["PatientId"].Width = 100;
dgvOrders.Columns["TestType"].Width = 150;
dgvOrders.Columns["SampleType"].Width = 80;
dgvOrders.Columns["Priority"].Width = 60;
dgvOrders.Columns["RequestTime"].Width = 120;
dgvOrders.Columns["Doctor"].Width = 80;
gbOrders.Controls.Add(dgvOrders);
// 右侧信息面板
Panel rightPanel = new Panel
{
Dock = DockStyle.Right,
Width = 350,
BackColor = Color.White
};
// 打印机状态
GroupBox gbPrinterStatus = new GroupBox
{
Text = "打印机状态",
Location = new Point(10, 20),
Size = new Size(330, 150),
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
lblPrinter1Status = new Label
{
Text = "主打印机: 在线",
Location = new Point(20, 30),
Size = new Size(200, 25),
ForeColor = Color.Green
};
lblPrinter2Status = new Label
{
Text = "备用打印机: 离线",
Location = new Point(20, 60),
Size = new Size(200, 25),
ForeColor = Color.Red
};
lblPaperStatus = new Label
{
Text = "标签纸余量: 充足",
Location = new Point(20, 90),
Size = new Size(200, 25),
ForeColor = Color.Green
};
gbPrinterStatus.Controls.Add(lblPrinter1Status);
gbPrinterStatus.Controls.Add(lblPrinter2Status);
gbPrinterStatus.Controls.Add(lblPaperStatus);
// 今日统计
GroupBox gbTodayStats = new GroupBox
{
Text = "今日统计",
Location = new Point(10, 190),
Size = new Size(330, 200),
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
lblStatsBlood = new Label
{
Text = "血液标本: 156 个",
Location = new Point(20, 30),
Size = new Size(200, 25)
};
lblStatsUrine = new Label
{
Text = "尿液标本: 89 个",
Location = new Point(20, 60),
Size = new Size(200, 25)
};
lblStatsStool = new Label
{
Text = "粪便标本: 23 个",
Location = new Point(20, 90),
Size = new Size(200, 25)
};
lblStatsOther = new Label
{
Text = "其他标本: 45 个",
Location = new Point(20, 120),
Size = new Size(200, 25)
};
lblStatsTotal = new Label
{
Text = "总计: 313 个",
Location = new Point(20, 150),
Size = new Size(200, 25),
Font = new Font("微软雅黑", 10, FontStyle.Bold),
ForeColor = Color.Blue
};
gbTodayStats.Controls.Add(lblStatsBlood);
gbTodayStats.Controls.Add(lblStatsUrine);
gbTodayStats.Controls.Add(lblStatsStool);
gbTodayStats.Controls.Add(lblStatsOther);
gbTodayStats.Controls.Add(lblStatsTotal);
// 最近打印记录
GroupBox gbRecentPrints = new GroupBox
{
Text = "最近打印记录",
Location = new Point(10, 410),
Size = new Size(330, 200),
Font = new Font("微软雅黑", 10, FontStyle.Bold)
};
lstRecentPrints = new ListView
{
Location = new Point(10, 25),
Size = new Size(310, 160),
View = View.Details,
FullRowSelect = true,
GridLines = true
};
lstRecentPrints.Columns.Add("时间", 60);
lstRecentPrints.Columns.Add("申请单号", 100);
lstRecentPrints.Columns.Add("患者", 80);
lstRecentPrints.Columns.Add("状态", 50);
gbRecentPrints.Controls.Add(lstRecentPrints);
rightPanel.Controls.Add(gbPrinterStatus);
rightPanel.Controls.Add(gbTodayStats);
rightPanel.Controls.Add(gbRecentPrints);
mainPanel.Controls.Add(gbQuickActions);
mainPanel.Controls.Add(gbOrders);
this.Controls.Add(statusPanel);
this.Controls.Add(navPanel);
this.Controls.Add(mainPanel);
this.Controls.Add(rightPanel);
}
private void InitializeSystem()
{
// 启动定时刷新
refreshTimer = new Timer { Interval = 5000 };
refreshTimer.Tick += RefreshTimer_Tick;
refreshTimer.Start();
// 加载初始数据
LoadPendingOrders();
LoadTodayStats();
LoadPrinterStatus();
LoadRecentPrints();
}
#region 事件处理
private void NavigateToForm(string formName)
{
Form form = null;
switch (formName)
{
case "打印控制台":
form = new PrintDashboard();
break;
case "模板设计":
form = new TemplateDesigner();
break;
case "打印机管理":
form = new PrinterManager();
break;
case "申请单监控":
form = new OrderMonitor();
break;
case "标本追踪":
form = new SpecimenTrackingForm();
break;
case "系统设置":
form = new SettingsForm();
break;
}
if (form != null)
{
form.Show();
}
}
private void RefreshTimer_Tick(object sender, EventArgs e)
{
LoadPendingOrders();
LoadPrinterStatus();
UpdateServerStatus();
}
private void BtnPrintSelected_Click(object sender, EventArgs e)
{
if (dgvOrders.SelectedRows.Count == 0)
{
MessageBox.Show("请选择要打印的申请单!", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
foreach (DataGridViewRow row in dgvOrders.SelectedRows)
{
string orderNo = row.Cells["OrderNo"].Value.ToString();
PrintOrder(orderNo);
}
LoadPendingOrders();
LoadTodayStats();
}
private void BtnReprint_Click(object sender, EventArgs e)
{
using (ReprintForm form = new ReprintForm())
{
if (form.ShowDialog() == DialogResult.OK)
{
string orderNo = form.SelectedOrderNo;
PrintOrder(orderNo, true); // 补打标记
}
}
}
private void BtnScanPrint_Click(object sender, EventArgs e)
{
using (ScanPrintForm form = new ScanPrintForm())
{
form.ShowDialog();
}
}
private void BtnBatchPrint_Click(object sender, EventArgs e)
{
using (BatchPrintForm form = new BatchPrintForm())
{
if (form.ShowDialog() == DialogResult.OK)
{
BatchPrintOrders(form.SelectedOrders);
}
}
}
#endregion
#region 业务逻辑
private void LoadPendingOrders()
{
try
{
var orders = DatabaseManager.GetPendingOrders();
dgvOrders.Rows.Clear();
foreach (var order in orders)
{
int rowIndex = dgvOrders.Rows.Add();
DataGridViewRow row = dgvOrders.Rows[rowIndex];
row.Cells["OrderNo"].Value = order.OrderNo;
row.Cells["PatientName"].Value = order.PatientName;
row.Cells["PatientId"].Value = order.PatientId;
row.Cells["TestType"].Value = order.TestType;
row.Cells["SampleType"].Value = order.SampleType;
row.Cells["Priority"].Value = order.Priority;
row.Cells["RequestTime"].Value = order.RequestTime.ToString("MM-dd HH:mm");
row.Cells["Doctor"].Value = order.Doctor;
// 设置行颜色
if (order.Priority == "紧急")
{
row.DefaultCellStyle.BackColor = Color.LightPink;
}
else if (order.Priority == "加急")
{
row.DefaultCellStyle.BackColor = Color.LightYellow;
}
}
pendingOrdersCount = orders.Count;
lblPendingOrders.Text = $"待打印申请: {pendingOrdersCount}";
}
catch (Exception ex)
{
Logger.Error($"加载待打印申请失败: {ex.Message}");
}
}
private void LoadTodayStats()
{
try
{
var stats = DatabaseManager.GetTodayStats();
lblStatsBlood.Text = $"血液标本: {stats.BloodCount} 个";
lblStatsUrine.Text = $"尿液标本: {stats.UrineCount} 个";
lblStatsStool.Text = $"粪便标本: {stats.StoolCount} 个";
lblStatsOther.Text = $"其他标本: {stats.OtherCount} 个";
lblStatsTotal.Text = $"总计: {stats.TotalCount} 个";
todayPrintCount = stats.TotalCount;
lblTodayPrints.Text = $"今日打印: {todayPrintCount}";
}
catch (Exception ex)
{
Logger.Error($"加载今日统计失败: {ex.Message}");
}
}
private void LoadPrinterStatus()
{
try
{
var status = PrinterManager.GetPrinterStatus();
lblPrinter1Status.Text = $"主打印机: {(status.Printer1Online ? "在线" : "离线")}";
lblPrinter1Status.ForeColor = status.Printer1Online ? Color.Green : Color.Red;
lblPrinter2Status.Text = $"备用打印机: {(status.Printer2Online ? "在线" : "离线")}";
lblPrinter2Status.ForeColor = status.Printer2Online ? Color.Green : Color.Red;
lblPaperStatus.Text = $"标签纸余量: {(status.PaperSufficient ? "充足" : "不足")}";
lblPaperStatus.ForeColor = status.PaperSufficient ? Color.Green : Color.Red;
}
catch (Exception ex)
{
Logger.Error($"加载打印机状态失败: {ex.Message}");
}
}
private void LoadRecentPrints()
{
try
{
var prints = DatabaseManager.GetRecentPrints(10);
lstRecentPrints.Items.Clear();
foreach (var print in prints)
{
ListViewItem item = new ListViewItem(print.PrintTime.ToString("HH:mm"));
item.SubItems.Add(print.OrderNo);
item.SubItems.Add(print.PatientName);
item.SubItems.Add(print.Status);
lstRecentPrints.Items.Add(item);
}
}
catch (Exception ex)
{
Logger.Error($"加载最近打印记录失败: {ex.Message}");
}
}
private void UpdateServerStatus()
{
bool connected = LisInterface.CheckConnection();
lblServerStatus.Text = $"LIS连接: {(connected ? "正常" : "断开")}";
lblServerStatus.ForeColor = connected ? Color.LightGreen : Color.Red;
}
private void PrintOrder(string orderNo, bool isReprint = false)
{
try
{
// 获取申请信息
TestOrder order = DatabaseManager.GetOrderByNo(orderNo);
if (order == null)
{
MessageBox.Show($"未找到申请单: {orderNo}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 生成条码
string barcode = BarcodeGenerator.GenerateBarcode(order);
// 获取标签模板
LabelTemplate template = TemplateManager.GetTemplate(order.SampleType);
// 创建打印任务
PrintJob job = new PrintJob
{
OrderNo = orderNo,
Barcode = barcode,
Template = template,
PatientName = order.PatientName,
PatientId = order.PatientId,
TestType = order.TestType,
SampleType = order.SampleType,
Priority = order.Priority,
IsReprint = isReprint,
PrintTime = DateTime.Now
};
// 发送到打印机
bool success = LabelPrinter.Print(job);
if (success)
{
// 记录打印日志
DatabaseManager.LogPrintJob(job);
// 更新申请状态
DatabaseManager.UpdateOrderStatus(orderNo, "已打印");
// 更新标本追踪
SpecimenTracker.TrackSpecimen(orderNo, "已打印条码");
Logger.Info($"打印成功: {orderNo}");
}
else
{
Logger.Error($"打印失败: {orderNo}");
MessageBox.Show($"打印失败,请检查打印机!", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception ex)
{
Logger.Error($"打印申请单 {orderNo} 失败: {ex.Message}");
MessageBox.Show($"打印失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BatchPrintOrders(List<string> orderNos)
{
int successCount = 0;
int failCount = 0;
foreach (string orderNo in orderNos)
{
try
{
PrintOrder(orderNo);
successCount++;
}
catch
{
failCount++;
}
}
MessageBox.Show($"批量打印完成!\n成功: {successCount} 个\n失败: {failCount} 个",
"批量打印结果", MessageBoxButtons.OK, MessageBoxIcon.Information);
LoadPendingOrders();
LoadTodayStats();
}
#endregion
// 控件声明
private Label lblSystemTitle;
private Label lblPendingOrders;
private Label lblTodayPrints;
private Label lblServerStatus;
private DataGridView dgvOrders;
private Label lblPrinter1Status;
private Label lblPrinter2Status;
private Label lblPaperStatus;
private Label lblStatsBlood;
private Label lblStatsUrine;
private Label lblStatsStool;
private Label lblStatsOther;
private Label lblStatsTotal;
private ListView lstRecentPrints;
private Button btnPrintSelected;
private Button btnReprint;
private Button btnScanPrint;
private Button btnBatchPrint;
}
}
3. LIS 接口核心类 (Core/LisInterface.cs)
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using LisBarcodePrinting.Models;
using LisBarcodePrinting.Utils;
namespace LisBarcodePrinting.Core
{
public static class LisInterface
{
private static Timer syncTimer;
private static bool isRunning = false;
public static void Start()
{
if (isRunning) return;
isRunning = true;
// 每30秒同步一次LIS数据
syncTimer = new Timer(SyncData, null, 0, 30000);
Logger.Info("LIS接口服务已启动");
}
public static void Stop()
{
if (!isRunning) return;
syncTimer?.Dispose();
isRunning = false;
Logger.Info("LIS接口服务已停止");
}
public static bool CheckConnection()
{
try
{
string connectionString = ConfigManager.LisConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
return true;
}
}
catch
{
return false;
}
}
private static void SyncData(object state)
{
try
{
Logger.Info("开始同步LIS数据...");
// 同步新的检验申请
SyncNewOrders();
// 同步患者信息
SyncPatientInfo();
// 同步检验结果(如果需要)
SyncTestResults();
Logger.Info("LIS数据同步完成");
}
catch (Exception ex)
{
Logger.Error($"LIS数据同步失败: {ex.Message}");
}
}
private static void SyncNewOrders()
{
string connectionString = ConfigManager.LisConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
// 查询过去24小时内新生成的检验申请
string sql = @"
SELECT
o.OrderNo,
o.PatientId,
o.DoctorId,
o.RequestTime,
o.Priority,
o.TestTypeCode,
o.SampleTypeCode,
o.Status,
p.PatientName,
p.Gender,
p.BirthDate,
p.Phone,
d.DoctorName
FROM TestOrders o
INNER JOIN Patients p ON o.PatientId = p.PatientId
INNER JOIN Doctors d ON o.DoctorId = d.DoctorId
WHERE o.RequestTime >= DATEADD(HOUR, -24, GETDATE())
AND o.Status = 'REQUESTED'
AND NOT EXISTS (
SELECT 1 FROM PrintedOrders po WHERE po.OrderNo = o.OrderNo
)
ORDER BY o.RequestTime";
using (SqlCommand cmd = new SqlCommand(sql, conn))
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
TestOrder order = new TestOrder
{
OrderNo = reader["OrderNo"].ToString(),
PatientId = reader["PatientId"].ToString(),
PatientName = reader["PatientName"].ToString(),
Gender = reader["Gender"].ToString(),
BirthDate = Convert.ToDateTime(reader["BirthDate"]),
Phone = reader["Phone"].ToString(),
DoctorId = reader["DoctorId"].ToString(),
DoctorName = reader["DoctorName"].ToString(),
RequestTime = Convert.ToDateTime(reader["RequestTime"]),
Priority = reader["Priority"].ToString(),
TestTypeCode = reader["TestTypeCode"].ToString(),
SampleTypeCode = reader["SampleTypeCode"].ToString(),
Status = reader["Status"].ToString()
};
// 保存到本地数据库
DatabaseManager.SaveOrder(order);
}
}
}
}
private static void SyncPatientInfo()
{
// 同步患者信息更新
string connectionString = ConfigManager.LisConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
string sql = @"
SELECT p.PatientId, p.PatientName, p.Gender, p.BirthDate, p.Phone
FROM Patients p
INNER JOIN TestOrders o ON p.PatientId = o.PatientId
WHERE o.RequestTime >= DATEADD(HOUR, -24, GETDATE())
GROUP BY p.PatientId, p.PatientName, p.Gender, p.BirthDate, p.Phone";
using (SqlCommand cmd = new SqlCommand(sql, conn))
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Patient patient = new Patient
{
PatientId = reader["PatientId"].ToString(),
PatientName = reader["PatientName"].ToString(),
Gender = reader["Gender"].ToString(),
BirthDate = Convert.ToDateTime(reader["BirthDate"]),
Phone = reader["Phone"].ToString()
};
DatabaseManager.SavePatient(patient);
}
}
}
}
private static void SyncTestResults()
{
// 同步检验结果(可选功能)
// 这里可以根据需要同步已完成的检验结果
}
// HL7消息处理(如果需要)
public static void ProcessHL7Message(string hl7Message)
{
try
{
// 解析HL7消息
// 这里需要根据具体的HL7版本和消息类型进行解析
Logger.Info($"收到HL7消息: {hl7Message.Substring(0, Math.Min(100, hl7Message.Length))}");
// 提取检验申请信息
// 保存到本地数据库
}
catch (Exception ex)
{
Logger.Error($"处理HL7消息失败: {ex.Message}");
}
}
// WebService接口(如果需要)
public static TestOrder GetOrderByNo(string orderNo)
{
try
{
string connectionString = ConfigManager.LisConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
string sql = @"
SELECT o.*, p.PatientName, p.Gender, p.BirthDate, p.Phone, d.DoctorName
FROM TestOrders o
INNER JOIN Patients p ON o.PatientId = p.PatientId
INNER JOIN Doctors d ON o.DoctorId = d.DoctorId
WHERE o.OrderNo = @OrderNo";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@OrderNo", orderNo);
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
return new TestOrder
{
OrderNo = reader["OrderNo"].ToString(),
PatientId = reader["PatientId"].ToString(),
PatientName = reader["PatientName"].ToString(),
Gender = reader["Gender"].ToString(),
BirthDate = Convert.ToDateTime(reader["BirthDate"]),
Phone = reader["Phone"].ToString(),
DoctorName = reader["DoctorName"].ToString(),
RequestTime = Convert.ToDateTime(reader["RequestTime"]),
Priority = reader["Priority"].ToString(),
TestTypeCode = reader["TestTypeCode"].ToString(),
SampleTypeCode = reader["SampleTypeCode"].ToString(),
Status = reader["Status"].ToString()
};
}
}
}
}
}
catch (Exception ex)
{
Logger.Error($"获取申请单 {orderNo} 失败: {ex.Message}");
}
return null;
}
}
}
4. 条码生成器 (Core/BarcodeGenerator.cs)
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using ThoughtWorks.QRCode.Codec;
using ZXing;
using ZXing.Common;
using ZXing.OneD;
namespace LisBarcodePrinting.Core
{
public static class BarcodeGenerator
{
// 生成Code128条码
public static Bitmap GenerateCode128(string content)
{
try
{
BarcodeWriter writer = new BarcodeWriter
{
Format = BarcodeFormat.CODE_128,
Options = new EncodingOptions
{
Height = 80,
Width = 300,
Margin = 10
}
};
return writer.Write(content);
}
catch (Exception ex)
{
Logger.Error($"生成Code128条码失败: {ex.Message}");
return null;
}
}
// 生成二维码
public static Bitmap GenerateQRCode(string content)
{
try
{
QRCodeEncoder encoder = new QRCodeEncoder();
encoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
encoder.QRCodeScale = 4;
encoder.QRCodeVersion = 0;
encoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.L;
return encoder.Encode(content);
}
catch (Exception ex)
{
Logger.Error($"生成二维码失败: {ex.Message}");
return null;
}
}
// 根据检验申请生成条码
public static string GenerateBarcode(TestOrder order)
{
// 条码规则:申请单号 + 标本类型 + 时间戳
string barcode = $"{order.OrderNo}{order.SampleTypeCode}{DateTime.Now:yyMMddHHmm}";
// 保存到数据库
DatabaseManager.SaveBarcode(order.OrderNo, barcode);
return barcode;
}
// 生成带文本的条码图片
public static Bitmap GenerateBarcodeWithText(string barcode, string text)
{
try
{
// 生成条码
Bitmap barcodeImage = GenerateCode128(barcode);
// 创建最终图片
int width = barcodeImage.Width;
int height = barcodeImage.Height + 40; // 额外空间用于文本
Bitmap finalImage = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(finalImage))
{
// 绘制条码
g.DrawImage(barcodeImage, 0, 0);
// 绘制文本
using (Font font = new Font("Arial", 10))
using (Brush brush = new SolidBrush(Color.Black))
{
StringFormat sf = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
Rectangle textRect = new Rectangle(0, barcodeImage.Height, width, 40);
g.DrawString(text, font, brush, textRect, sf);
}
}
return finalImage;
}
catch (Exception ex)
{
Logger.Error($"生成带文本条码失败: {ex.Message}");
return null;
}
}
// 生成标本标签图片
public static Bitmap GenerateSpecimenLabel(TestOrder order, string barcode)
{
try
{
// 标签尺寸:50mm x 30mm (约200px x 120px)
int width = 400;
int height = 240;
Bitmap labelImage = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(labelImage))
{
g.Clear(Color.White);
// 绘制边框
using (Pen pen = new Pen(Color.Black, 2))
{
g.DrawRectangle(pen, 0, 0, width - 1, height - 1);
}
// 绘制医院名称
using (Font font = new Font("微软雅黑", 12, FontStyle.Bold))
using (Brush brush = new SolidBrush(Color.Black))
{
StringFormat sf = new StringFormat
{
Alignment = StringAlignment.Center
};
Rectangle hospitalRect = new Rectangle(0, 10, width, 30);
g.DrawString("XX市人民医院检验科", font, brush, hospitalRect, sf);
}
// 绘制条码
Bitmap barcodeImage = GenerateCode128(barcode);
if (barcodeImage != null)
{
g.DrawImage(barcodeImage, 50, 50, 300, 80);
}
// 绘制申请单号
using (Font font = new Font("Arial", 10))
using (Brush brush = new SolidBrush(Color.Black))
{
g.DrawString($"申请单号: {order.OrderNo}", font, brush, 10, 140);
}
// 绘制患者信息
using (Font font = new Font("微软雅黑", 10))
using (Brush brush = new SolidBrush(Color.Black))
{
g.DrawString($"患者: {order.PatientName}", font, brush, 10, 165);
g.DrawString($"性别: {order.Gender}", font, brush, 200, 165);
}
// 绘制标本类型和检验项目
using (Font font = new Font("微软雅黑", 10))
using (Brush brush = new SolidBrush(Color.Black))
{
g.DrawString($"标本: {order.SampleTypeCode}", font, brush, 10, 190);
g.DrawString($"项目: {order.TestTypeCode}", font, brush, 10, 215);
}
}
return labelImage;
}
catch (Exception ex)
{
Logger.Error($"生成标本标签失败: {ex.Message}");
return null;
}
}
}
}
5. 标签打印管理 (Core/LabelPrinter.cs)
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Threading;
using LisBarcodePrinting.Models;
using LisBarcodePrinting.Utils;
namespace LisBarcodePrinting.Core
{
public static class LabelPrinter
{
private static PrintDocument printDocument;
private static PrintJob currentJob;
private static bool isPrinting = false;
static LabelPrinter()
{
printDocument = new PrintDocument();
printDocument.PrintPage += PrintDocument_PrintPage;
printDocument.EndPrint += PrintDocument_EndPrint;
}
public static bool Print(PrintJob job)
{
if (isPrinting)
{
Logger.Warning("打印机正忙,请稍后再试");
return false;
}
try
{
currentJob = job;
isPrinting = true;
// 设置打印机
string printerName = ConfigManager.DefaultPrinter;
if (!string.IsNullOrEmpty(printerName))
{
printDocument.PrinterSettings.PrinterName = printerName;
}
// 设置纸张大小
printDocument.DefaultPageSettings.PaperSize = new PaperSize("Label", 400, 240);
printDocument.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);
// 开始打印
printDocument.Print();
return true;
}
catch (Exception ex)
{
Logger.Error($"打印失败: {ex.Message}");
isPrinting = false;
return false;
}
}
private static void PrintDocument_PrintPage(object sender, PrintPageEventArgs e)
{
try
{
if (currentJob == null) return;
// 生成标签图片
Bitmap labelImage = BarcodeGenerator.GenerateSpecimenLabel(
new TestOrder
{
OrderNo = currentJob.OrderNo,
PatientName = currentJob.PatientName,
PatientId = currentJob.PatientId,
TestTypeCode = currentJob.TestType,
SampleTypeCode = currentJob.SampleType
},
currentJob.Barcode
);
if (labelImage != null)
{
// 绘制到打印机
e.Graphics.DrawImage(labelImage, 0, 0, e.PageBounds.Width, e.PageBounds.Height);
}
e.HasMorePages = false;
}
catch (Exception ex)
{
Logger.Error($"打印页面失败: {ex.Message}");
e.HasMorePages = false;
}
}
private static void PrintDocument_EndPrint(object sender, PrintEventArgs e)
{
isPrinting = false;
currentJob = null;
}
// 打印到文件(用于测试)
public static bool PrintToFile(PrintJob job, string filePath)
{
try
{
Bitmap labelImage = BarcodeGenerator.GenerateSpecimenLabel(
new TestOrder
{
OrderNo = job.OrderNo,
PatientName = job.PatientName,
PatientId = job.PatientId,
TestTypeCode = job.TestType,
SampleTypeCode = job.SampleType
},
job.Barcode
);
if (labelImage != null)
{
labelImage.Save(filePath, ImageFormat.Png);
return true;
}
return false;
}
catch (Exception ex)
{
Logger.Error($"打印到文件失败: {ex.Message}");
return false;
}
}
// 获取打印机状态
public static PrinterStatus GetPrinterStatus()
{
PrinterStatus status = new PrinterStatus();
try
{
string printerName = ConfigManager.DefaultPrinter;
if (!string.IsNullOrEmpty(printerName))
{
PrintServer printServer = new PrintServer();
PrintQueue printQueue = new PrintQueue(printServer, printerName);
status.Printer1Online = printQueue.IsOutOfPaper == false;
status.PaperSufficient = printQueue.IsPaperJammed == false;
}
}
catch (Exception ex)
{
Logger.Error($"获取打印机状态失败: {ex.Message}");
status.Printer1Online = false;
status.Printer2Online = false;
status.PaperSufficient = false;
}
return status;
}
// 打印测试页
public static bool PrintTestPage()
{
try
{
PrintJob testJob = new PrintJob
{
OrderNo = "TEST001",
Barcode = "123456789012",
PatientName = "测试患者",
PatientId = "P123456",
TestType = "血常规",
SampleType = "静脉血",
Priority = "普通"
};
return Print(testJob);
}
catch (Exception ex)
{
Logger.Error($"打印测试页失败: {ex.Message}");
return false;
}
}
}
public class PrinterStatus
{
public bool Printer1Online { get; set; }
public bool Printer2Online { get; set; }
public bool PaperSufficient { get; set; }
}
}
6. 数据模型 (Models/TestOrder.cs)
using System;
namespace LisBarcodePrinting.Models
{
public class TestOrder
{
public string OrderNo { get; set; }
public string PatientId { get; set; }
public string PatientName { get; set; }
public string Gender { get; set; }
public DateTime BirthDate { get; set; }
public string Phone { get; set; }
public string DoctorId { get; set; }
public string DoctorName { get; set; }
public DateTime RequestTime { get; set; }
public string Priority { get; set; } // 普通、加急、紧急
public string TestTypeCode { get; set; }
public string TestTypeName { get; set; }
public string SampleTypeCode { get; set; }
public string SampleTypeName { get; set; }
public string Status { get; set; } // REQUESTED, PRINTED, COLLECTED, TESTING, COMPLETED
public string Department { get; set; }
public string Ward { get; set; }
public string BedNo { get; set; }
public string Diagnosis { get; set; }
public string Remark { get; set; }
// 计算年龄
public int Age
{
get
{
DateTime now = DateTime.Now;
int age = now.Year - BirthDate.Year;
if (now.Month < BirthDate.Month || (now.Month == BirthDate.Month && now.Day < BirthDate.Day))
{
age--;
}
return age;
}
}
// 获取完整的患者信息
public string GetPatientInfo()
{
return $"{PatientName} ({Gender}) {Age}岁";
}
// 获取检验项目描述
public string GetTestDescription()
{
return $"{TestTypeName} ({SampleTypeName})";
}
}
public class Specimen
{
public string SpecimenId { get; set; }
public string OrderNo { get; set; }
public string Barcode { get; set; }
public string SampleType { get; set; }
public DateTime CollectTime { get; set; }
public string Collector { get; set; }
public string CollectorId { get; set; }
public string ContainerNo { get; set; }
public string Status { get; set; } // COLLECTED, RECEIVED, TESTING, COMPLETED
public string Remark { get; set; }
}
public class PrintJob
{
public string OrderNo { get; set; }
public string Barcode { get; set; }
public string Template { get; set; }
public string PatientName { get; set; }
public string PatientId { get; set; }
public string TestType { get; set; }
public string SampleType { get; set; }
public string Priority { get; set; }
public bool IsReprint { get; set; }
public DateTime PrintTime { get; set; }
public string Status { get; set; }
public string ErrorMessage { get; set; }
}
public class Patient
{
public string PatientId { get; set; }
public string PatientName { get; set; }
public string Gender { get; set; }
public DateTime BirthDate { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public string IdCard { get; set; }
public string InsuranceNo { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
}
7. 项目文件 (LisBarcodePrinting.csproj)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{YOUR-PROJECT-GUID}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>LisBarcodePrinting</RootNamespace>
<AssemblyName>LisBarcodePrinting</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Drawing" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data.SQLite">
<HintPath>..\packages\System.Data.SQLite.Core.1.0.117\lib\net472\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="ThoughtWorks.QRCode">
<HintPath>..\packages\ThoughtWorks.QRCode.1.1.0\lib\net40\ThoughtWorks.QRCode.dll</HintPath>
</Reference>
<Reference Include="ZXing">
<HintPath>..\packages\ZXing.Net.0.16.9\lib\net462\ZXing.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="MainForm.cs" />
<Compile Include="MainForm.Designer.cs" />
<Compile Include="Core\LisInterface.cs" />
<Compile Include="Core\BarcodeGenerator.cs" />
<Compile Include="Core\LabelPrinter.cs" />
<Compile Include="Core\TemplateEngine.cs" />
<Compile Include="Core\SpecimenTracker.cs" />
<Compile Include="Forms\PrintDashboard.cs" />
<Compile Include="Forms\TemplateDesigner.cs" />
<Compile Include="Forms\PrinterManager.cs" />
<Compile Include="Forms\OrderMonitor.cs" />
<Compile Include="Forms\SettingsForm.cs" />
<Compile Include="Models\TestOrder.cs" />
<Compile Include="Models\Specimen.cs" />
<Compile Include="Models\LabelTemplate.cs" />
<Compile Include="Models\PrintJob.cs" />
<Compile Include="Models\Patient.cs" />
<Compile Include="Data\DatabaseManager.cs" />
<Compile Include="Data\LisDataSync.cs" />
<Compile Include="Data\CacheManager.cs" />
<Compile Include="Hardware\BarcodeScanner.cs" />
<Compile Include="Hardware\LabelPrinter.cs" />
<Compile Include="Hardware\CardReader.cs" />
<Compile Include="Utils\ConfigManager.cs" />
<Compile Include="Utils\Logger.cs" />
<Compile Include="Utils\Validator.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
参考代码 医院实验室LIS条码打印管理系统 www.youwenfan.com/contentcsv/112209.html
三、系统功能说明
1. 核心功能模块
| 模块 |
功能描述 |
| LIS接口 |
实时同步检验申请、患者信息、检验结果 |
| 条码生成 |
支持Code128、二维码、自定义规则 |
| 标签打印 |
多打印机管理、自动选择、故障转移 |
| 模板设计 |
可视化标签模板设计器 |
| 标本追踪 |
从申请到报告的全程追踪 |
| 数据统计 |
打印量统计、标本类型分析 |
2. 支持的检验标本类型
| 标本类型 |
条码前缀 |
标签颜色 |
容器要求 |
| 静脉血 |
BL |
红色 |
真空采血管 |
| 动脉血 |
AR |
蓝色 |
血气针筒 |
| 尿液 |
UR |
黄色 |
尿杯 |
| 粪便 |
ST |
绿色 |
便盒 |
| 脑脊液 |
CS |
紫色 |
无菌管 |
| 胸腹水 |
PF |
橙色 |
引流袋 |
3. 条码规则设计
条码格式: [申请单号][标本类型][时间戳]
示例: ORD20231227001BL2312271430
其中:
- ORD20231227001: 申请单号
- BL: 标本类型代码(静脉血)
- 2312271430: 年月日时分
4. 标签模板示例
┌─────────────────────────────┐
│ XX市人民医院检验科 │
├─────────────────────────────┤
│ ████████████████████████ │ ← 条码
│ ████████████████████████ │
├─────────────────────────────┤
│ 申请单号: ORD20231227001 │
│ 患者: 张三 (男) 45岁 │
│ 标本: 静脉血 │
│ 项目: 血常规+生化 │
│ 申请时间: 2023-12-27 14:30 │
│ 申请医生: 李医生 │
└─────────────────────────────┘
5. 硬件配置建议
| 设备 |
型号推荐 |
数量 |
| 条码打印机 |
Zebra ZD420/ZD620 |
2台 |
| 条码扫描枪 |
Honeywell 1900/1910 |
3把 |
| 就诊卡读卡器 |
明华USB读卡器 |
2个 |
| 标签纸 |
50mm×30mm热敏标签 |
若干 |
| 工控机 |
研华/华北工控 |
1台 |
6. 部署注意事项
- 数据库权限:确保LIS数据库只读权限
- 网络安全:防火墙开放1433端口
- 打印机驱动:提前安装好打印机驱动
- 标签纸校准:首次使用前校准标签纸
- 备份策略:定期备份打印记录
7. 扩展功能建议
- 移动端支持:开发手机APP扫码接收标本
- AI识别:自动识别标本质量
- 冷链监控:监控标本运输温度
- 电子签名:检验师电子签名确认
- 智能分拣:自动分拣标本到对应仪器