在医药行业信息化快速发展的背景下,传统依赖人工台账和Excel表格的药品管理模式日益暴露出数据孤岛、更新滞后、差错率高等痛点。一款基于SSH(Struts2 + Spring + Hibernate)集成框架的药品信息管理系统应运而生,该系统被命名为“医药物联数据中枢”,旨在为医院药房、药品流通企业及医疗机构提供一体化的药品数据管理解决方案。系统通过数字化手段将药品的入库、出库、库存、有效期及基础信息进行集中化、规范化管理,显著提升了工作效率并降低了人为差错风险。
系统采用经典的三层架构设计。表现层使用Struts2框架处理用户交互,通过Action类接收前端请求并调用业务逻辑;业务层基于Spring框架的IoC容器进行Bean管理,利用声明式事务管理确保药品库存变更等核心操作的数据一致性;持久层则依托Hibernate实现对象关系映射(ORM),将药品、库存、供应商等实体类与数据库表映射,简化了数据持久化操作。
在数据库设计方面,系统选用MySQL作为数据存储引擎,通过五张核心表构建了完整的业务数据模型。其中tb_medicine(药品表)作为系统的核心数据载体,其结构设计体现了严谨的业务逻辑:
CREATE TABLE `tb_medicine` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`medNo` varchar(255) NOT NULL COMMENT '药品编号',
`name` varchar(255) NOT NULL COMMENT '名字',
`factoryAdd` varchar(255) DEFAULT NULL COMMENT '厂家地址',
`description` longtext DEFAULT NULL COMMENT '描述',
`price` double NOT NULL COMMENT '价格',
`medCount` int(11) DEFAULT NULL COMMENT '药品数量',
`reqCount` int(11) DEFAULT NULL COMMENT '需求数量',
`photoPath` varchar(255) DEFAULT NULL COMMENT '图片路径',
`categoryId` int(11) DEFAULT NULL COMMENT '分类ID',
PRIMARY KEY (`id`),
UNIQUE KEY `medNo` (`medNo`),
KEY `FKCB73D4EB5CE1611D` (`categoryId`),
CONSTRAINT `FKCB73D4EB5CE1611D` FOREIGN KEY (`categoryId`) REFERENCES `tb_category` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='药品表'
该表设计中的medNo字段设置了唯一索引,确保药品编号的唯一性;categoryId外键关联分类表,建立了药品与分类的层级关系;medCount和reqCount分别记录实际库存和需求数量,为库存预警功能提供数据支撑。
销售明细表tb_selldetail的设计同样体现了业务完整性:
CREATE TABLE `tb_selldetail` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`sellName` varchar(255) NOT NULL COMMENT '销售名称',
`sellPrice` double NOT NULL COMMENT '销售价格',
`sellCount` int(11) NOT NULL COMMENT '销售数量',
`sellTime` datetime NOT NULL COMMENT '销售时间',
`medid` int(11) DEFAULT NULL COMMENT '药品ID',
`userid` int(11) DEFAULT NULL COMMENT '用户ID',
PRIMARY KEY (`id`),
KEY `FK56C63894DD16E7A7` (`medid`),
KEY `FK56C63894822F277` (`userid`),
CONSTRAINT `FK56C63894822F277` FOREIGN KEY (`userid`) REFERENCES `tb_user` (`id`),
CONSTRAINT `FK56C63894DD16E7A7` FOREIGN KEY (`medid`) REFERENCES `tb_medicine` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='销售明细表'
该表通过medid和userid两个外键分别关联药品表和用户表,完整记录了每笔销售业务的详细信息,为销售统计和业绩分析提供了数据基础。
在实体模型设计上,系统采用面向对象的方式封装业务数据。药品实体类Medicine的Hibernate映射配置如下:
@Entity
@Table(name = "tb_medicine")
public class Medicine implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "medNo", unique = true, nullable = false)
private String medicineNumber;
@Column(nullable = false)
private String name;
private String factoryAdd;
@Lob
private String description;
@Column(nullable = false)
private Double price;
private Integer medCount;
private Integer reqCount;
private String photoPath;
@ManyToOne
@JoinColumn(name = "categoryId")
private Category category;
// 省略getter/setter方法
}
对应的DAO层实现采用Hibernate Template进行数据访问,基础CRUD操作封装在MedicineDao类中:
@Repository
public class MedicineDao extends HibernateDaoSupport {
@Autowired
public void setSessionFactory0(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}
public void save(Medicine medicine) {
getHibernateTemplate().save(medicine);
}
public void update(Medicine medicine) {
getHibernateTemplate().update(medicine);
}
public void delete(Integer id) {
Medicine medicine = getHibernateTemplate().get(Medicine.class, id);
if (medicine != null) {
getHibernateTemplate().delete(medicine);
}
}
public Medicine findById(Integer id) {
return getHibernateTemplate().get(Medicine.class, id);
}
public List<Medicine> findAll() {
return (List<Medicine>) getHibernateTemplate().find("from Medicine");
}
}
业务逻辑层通过Spring的声明式事务管理确保数据一致性,MedicineService类封装了复杂的业务规则:
@Service
@Transactional
public class MedicineService {
@Autowired
private MedicineDao medicineDao;
@Transactional(readOnly = true)
public List<Medicine> getMedicinesByCategory(Integer categoryId) {
String hql = "from Medicine m where m.category.id = ?";
return medicineDao.find(hql, categoryId);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void updateStock(Integer medicineId, Integer quantity) {
Medicine medicine = medicineDao.findById(medicineId);
if (medicine != null) {
Integer currentStock = medicine.getMedCount();
medicine.setMedCount(currentStock + quantity);
medicineDao.update(medicine);
}
}
public List<Medicine> getLowStockMedicines() {
String hql = "from Medicine m where m.medCount < m.reqCount";
return medicineDao.find(hql);
}
}
表现层通过Struts2 Action处理用户请求,MedicineAction类负责接收前端参数并调用业务逻辑:
public class MedicineAction extends ActionSupport {
private Medicine medicine;
private List<Medicine> medicineList;
private Integer categoryId;
@Autowired
private MedicineService medicineService;
public String list() {
medicineList = medicineService.findAll();
return SUCCESS;
}
public String save() {
medicineService.save(medicine);
return SUCCESS;
}
public String findByCategory() {
if (categoryId != null) {
medicineList = medicineService.getMedicinesByCategory(categoryId);
}
return SUCCESS;
}
// 省略getter/setter方法
}
系统核心功能模块包括药品信息管理、库存管理、销售管理和统计分析。药品信息管理模块提供完整的药品档案维护功能,支持药品信息的增删改查操作。

库存管理模块实现实时库存监控和预警功能,通过HQL查询实现库存状态分析:
public class InventoryService {
public List<Object[]> getStockStatusReport() {
String hql = "select m.name, m.medCount, m.reqCount, " +
"case when m.medCount < m.reqCount then '缺货' " +
"when m.medCount < m.reqCount * 1.2 then '预警' " +
"else '正常' end as status " +
"from Medicine m";
return medicineDao.findByHQL(hql);
}
public List<Medicine> getExpiringMedicines(Date startDate, Date endDate) {
String hql = "from Medicine m where m.expireDate between ? and ?";
return medicineDao.find(hql, startDate, endDate);
}
}
销售管理模块记录每笔销售业务,确保库存数据的实时更新:

采购管理模块根据库存预警自动生成采购计划:

统计分析模块提供多维度数据展示,包括销售排名和分类统计:
public class StatisticsService {
public List<Object[]> getSalesRanking(Date startDate, Date endDate) {
String hql = "select sd.medicine.name, sum(sd.sellCount), sum(sd.sellCount * sd.sellPrice) " +
"from SellDetail sd where sd.sellTime between ? and ? " +
"group by sd.medicine.id, sd.medicine.name " +
"order by sum(sd.sellCount) desc";
return sellDetailDao.find(hql, startDate, endDate);
}
public List<Object[]> getCategoryStatistics() {
String hql = "select c.name, count(m.id), sum(m.medCount) " +
"from Category c left join c.medicines m " +
"group by c.id, c.name";
return categoryDao.findByHQL(hql);
}
}


系统在用户权限管理方面采用基于角色的访问控制,UserAction处理用户登录和权限验证:
public class UserAction extends ActionSupport {
private String username;
private String password;
private User user;
public String login() {
User existUser = userService.findByUsernameAndPassword(username, password);
if (existUser != null) {
ActionContext.getContext().getSession().put("user", existUser);
return SUCCESS;
} else {
addActionError("用户名或密码错误");
return INPUT;
}
}
public String logout() {
ActionContext.getContext().getSession().remove("user");
return SUCCESS;
}
}
在数据持久化方面,系统通过Hibernate的缓存机制提升查询性能。二级缓存配置在ehcache.xml中定义:
<cache name="com.example.Medicine"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false" />
Spring的配置文件applicationContext.xml中集成了Hibernate和事务管理:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>com/example/Medicine.hbm.xml</value>
<value>com/example/Category.hbm.xml</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
系统在性能优化方面采取了多项措施。数据库连接池配置使用C3P0:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/drug_db?useUnicode=true&characterEncoding=utf8"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="maxPoolSize" value="50"/>
<property name="minPoolSize" value="5"/>
<property name="initialPoolSize" value="10"/>
<property name="maxIdleTime" value="300"/>
</bean>
对于未来优化方向,系统可以考虑以下几个方面的改进:引入Redis缓存热点数据提升查询性能,实现药品批次管理和先进先出策略,开发移动端应用支持移动盘点,集成条形码或二维码扫描功能简化操作流程,以及增加数据可视化大屏展示实时经营数据。
在批次管理方面,可以扩展tb_medicine表结构,增加批次相关信息:
ALTER TABLE `tb_medicine`
ADD COLUMN `batchNo` VARCHAR(50) COMMENT '批次号',
ADD COLUMN `productionDate` DATE COMMENT '生产日期',
ADD COLUMN `expireDate` DATE COMMENT '失效日期',
ADD INDEX `idx_batch_expire` (`batchNo`, `expireDate`);
移动端接口可以通过Spring MVC实现RESTful API:
@RestController
@RequestMapping("/api/medicine")
public class MedicineRestController {
@Autowired
private MedicineService medicineService;
@GetMapping("/{id}")
public ResponseEntity<Medicine> getMedicine(@PathVariable Integer id) {
Medicine medicine = medicineService.findById(id);
return ResponseEntity.ok(medicine);
}
@PostMapping("/scan")
public ResponseEntity<Medicine> scanMedicine(@RequestParam String barcode) {
Medicine medicine = medicineService.findByBarcode(barcode);
return ResponseEntity.ok(medicine);
}
}
数据可视化大屏可以通过ECharts集成展示关键指标:
function initSalesChart() {
var chart = echarts.init(document.getElementById('sales-chart'));
$.get('/api/statistics/sales-trend', function(data) {
chart.setOption({
title: { text: '销售趋势分析' },
tooltip: { trigger: 'axis' },
xAxis: { data: data.dates },
yAxis: { type: 'value' },
series: [{ name: '销售额', type: 'line', data: data.amounts }]
});
});
}
系统通过严谨的架构设计和完善的功能模块,为医药行业提供了可靠的数字化管理解决方案。基于SSH框架的技术选型确保了系统的稳定性和可维护性,而模块化的设计则为后续功能扩展奠定了良好基础。