引言

随着医疗信息化向基层机构渗透,中小型药房与社区诊所对轻量化药品管理工具的需求持续增长。传统商业管理系统普遍存在部署成本高、功能冗余、定制难度大的问题,而基于嵌入式数据库的桌面应用方案,凭借零配置、易维护、低成本的优势,成为小型机构信息化的合适选择。

本文基于Python技术栈,结合PyQt5图形框架与SQLite嵌入式数据库,设计并实现了一套轻量级药房管理系统。系统采用分层架构设计,重点解决了销售操作数据一致性、多模块界面解耦、效期数据可视化预警等核心技术问题,具备良好的实用性与可扩展性。

一、系统整体架构与技术选型

1.1 分层架构设计

系统采用经典的三层架构模式,实现关注点分离,提升代码可维护性:

  • 表示层:基于PyQt5控件构建,负责用户交互与数据展示。主窗口采用标签页式布局,整合各业务模块,通过信号/槽机制与业务层通信,界面样式通过QSS统一管理,实现表现与逻辑分离。

  • 业务逻辑层:封装业务规则与流程控制,负责参数校验、业务编排、异常处理,协调数据层完成业务操作,是系统的核心调度层。

  • 数据访问层:通过DatabaseManager类封装所有数据库操作,对外提供领域化接口,内部屏蔽SQL细节与连接管理,采用参数化查询保障数据安全,通过事务保障写操作的原子性。

横向支撑模块包括工具函数库(日期处理、格式校验、数值计算)与样式管理模块,为各层提供通用能力。

1.2 技术选型与理由

技术选型 选型理由
Python 3.8 语法简洁,开发效率高,标准库功能完善,内置sqlite3模块无需额外依赖
PyQt5 控件库丰富,文档完善,跨平台兼容性好,信号/槽机制适合模块化桌面开发
SQLite 嵌入式零配置数据库,单文件存储便于备份迁移,支持事务与标准SQL,适配单机应用场景
PyQtChart Qt官方图表组件,原生集成度高,支持多种图表类型与动画效果,满足数据可视化需求
QSS样式表 语法类似CSS,可集中管理界面样式,支持差异化控件定制,便于主题调整

1.3 核心业务流程

系统核心业务围绕药品全生命周期管理展开:药品信息录入 → 库存维护 → 销售登记(事务性扣减库存+记录流水) → 效期监控预警 → 销售数据统计分析。所有写操作均经过业务校验与事务封装,确保数据完整性。

二、核心模块深度实现

2.1 事务性销售数据处理

销售操作是系统的核心写场景,涉及药品表库存更新与销售历史表记录写入两个关联操作,必须保证原子性,避免出现数据不一致。

设计思路

利用SQLite的事务支持,将两个更新操作放在同一事务上下文中执行。通过上下文管理器封装数据库连接,正常退出时自动提交事务,发生异常时自动回滚,从机制上保证数据一致性。同时在销售历史表中冗余存储药品快照信息,即使后续药品记录被删除,历史销售数据仍可完整追溯。

核心实现

def record_sale(self, sale_data):
"""
事务性提交销售订单
:param sale_data: 包含drug_id, sell_quantity, sell_price, sell_amount, sell_date的字典
:return: 执行成功返回True
"""
with self.get_connection() as conn:
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# 查询药品当前状态
cursor.execute("SELECT id, stock, drug_name, spec, batch_no FROM drugs WHERE id = ?",
(sale_data['drug_id'],))
drug_info = cursor.fetchone()
if not drug_info:
raise ValueError("目标药品不存在")
if drug_info['stock'] < sale_data['sell_quantity']:
raise ValueError("库存不足,无法完成销售")
# 计算库存变化
remain_stock = drug_info['stock'] - sale_data['sell_quantity']
# 更新药品主表
cursor.execute("""
UPDATE drugs
SET stock = ?,
sell_quantity = sell_quantity + ?,
sell_amount = sell_amount + ?,
sell_date = ?
WHERE id = ?
""", (remain_stock, sale_data['sell_quantity'],
sale_data['sell_amount'], sale_data['sell_date'],
sale_data['drug_id']))
# 写入销售流水,冗余存储药品快照
cursor.execute("""
INSERT INTO sales_history (
drug_id, drug_name, spec, batch_no,
sell_quantity, sell_price, sell_amount,
sell_date, original_stock, remaining_stock
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
drug_info['id'], drug_info['drug_name'], drug_info['spec'], drug_info['batch_no'],
sale_data['sell_quantity'], sale_data['sell_price'], sale_data['sell_amount'],
sale_data['sell_date'], drug_info['stock'], remain_stock
))
return True
调优说明
  • 采用参数化查询,避免SQL注入风险

  • 单次事务内完成两次写操作,减少连接开销

  • 流水表保留库存前后快照,满足审计与问题排查需求

2.2 基于信号/槽的模块化通信

桌面应用中多模块数据同步是常见难点,直接跨模块调用会导致代码耦合严重,难以维护。系统利用PyQt5的信号/槽机制,实现模块间的低耦合通信。

设计思路

为每个功能模块定义独立的更新信号,当某一模块执行了影响全局数据的操作(如完成销售)时,仅需发射对应信号,其他关联模块监听信号并执行自身的数据刷新,无需知道信号发射方的内部实现。

实现方案

class MainWindow(QMainWindow):
# 定义全局数据更新信号
drug_data_changed = pyqtSignal()
sales_data_changed = pyqtSignal()
def __init__(self, db_manager, username):
super().__init__()
self.db_manager = db_manager
self.username = username
# 信号绑定:销售数据变更后刷新药品表与效期表
self.sales_data_changed.connect(self.refresh_drug_table)
self.sales_data_changed.connect(self.refresh_expiry_table)
def submit_sales(self):
# 销售提交逻辑
if self.db_manager.record_sale(sale_data):
# 发射数据变更信号,通知关联模块刷新
self.sales_data_changed.emit()
QMessageBox.information(self, "成功", "销售登记完成")

更多推荐