在医药流通行业,传统的人工记录方式面临着效率低下、数据易出错、库存状态不透明等核心业务痛点。医药企业需要对药品的采购、销售和库存进行全面精准的管理,以确保药品安全、控制成本并满足行业监管要求。针对这一市场需求,我们开发了一套医药供应链智能管理平台,通过数字化手段实现药品全生命周期的精细化管控。
本系统采用经典的SSM(Spring+Spring MVC+MyBatis)框架组合,构建了一个稳定可靠的企业级管理解决方案。Spring框架负责业务对象的管理和依赖注入,通过IoC容器统一调度Service层业务逻辑;Spring MVC作为Web层框架,以DispatcherServlet为核心控制器,清晰划分了Controller、Service、Dao三层架构;MyBatis作为数据持久层解决方案,通过XML映射文件将Java对象与数据库表字段灵活绑定。
系统架构与技术栈深度解析
在技术架构设计上,系统采用分层架构模式,确保各层职责分明。表现层使用JSP+JSTL标签库结合Bootstrap前端框架,实现响应式界面设计;控制层通过Spring MVC的注解驱动开发,简化请求映射和参数绑定;业务层采用Spring的声明式事务管理,确保进销存操作的数据一致性;持久层利用MyBatis的动态SQL能力,灵活处理复杂的查询条件。
@Controller
@RequestMapping("/medicine")
public class MedicineController {
@Autowired
private MedicineService medicineService;
/**
* 药品分页查询
*/
@RequestMapping("/list")
public String list(@RequestParam(defaultValue="1")Integer pageNum,
@RequestParam(defaultValue="10")Integer pageSize,
Model model) {
PageInfo<Medicine> pageInfo = medicineService.findByPage(pageNum, pageSize);
model.addAttribute("pageInfo", pageInfo);
return "medicine/list";
}
/**
* 药品入库操作
*/
@RequestMapping("/stockIn")
@ResponseBody
public Map<String, Object> stockIn(@RequestBody InOrder inOrder) {
Map<String, Object> result = new HashMap<>();
try {
medicineService.stockIn(inOrder);
result.put("success", true);
result.put("message", "入库成功");
} catch (Exception e) {
result.put("success", false);
result.put("message", "入库失败:" + e.getMessage());
}
return result;
}
}
数据库设计亮点分析
药品表(t_medicine)的精细化设计
药品作为系统的核心实体,其表结构设计体现了医药行业的专业要求:
CREATE TABLE `t_medicine` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`cName` varchar(20) NOT NULL COMMENT '药品中文名',
`eName` varchar(20) NOT NULL COMMENT '药品英文名',
`price` double(11,2) NOT NULL COMMENT '药品价格',
`nums` int(11) NOT NULL COMMENT '库存数量',
`manufacturer` varchar(30) NOT NULL COMMENT '生产厂家',
`describle` varchar(100) NOT NULL COMMENT '药品描述',
`productDate` date NOT NULL COMMENT '生产日期',
`safeDate` varchar(20) NOT NULL COMMENT '保质期',
`standard` varchar(50) NOT NULL COMMENT '药品规格',
`typeId` int(11) NOT NULL COMMENT '药品类型ID',
`oldPrice` double(11,2) NOT NULL COMMENT '原价',
PRIMARY KEY (`id`),
KEY `idx_type` (`typeId`),
KEY `idx_name` (`cName`)
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='药品表'
该表设计的亮点包括:
- 双语言支持:同时记录药品的中文名和英文名,满足国际化需求
- 价格追踪:通过
price和oldPrice字段实现价格变动记录 - 安全管控:
productDate和safeDate确保药品有效期管理 - 索引优化:为类型ID和药品名称建立索引,提升查询效率
销售订单表(t_sellorder)的事务完整性设计
销售订单表的设计重点考虑了业务事务的完整性:
CREATE TABLE `t_sellorder` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`cName` varchar(40) NOT NULL COMMENT '药品中文名',
`price` double(11,2) NOT NULL COMMENT '销售价格',
`buyNums` int(11) NOT NULL COMMENT '购买数量',
`totalMoney` double(11,2) NOT NULL COMMENT '总金额',
`createDate` datetime NOT NULL COMMENT '创建日期',
`status` int(11) NOT NULL COMMENT '订单状态',
`userId` int(11) NOT NULL COMMENT '用户ID',
PRIMARY KEY (`id`),
KEY `idx_user_date` (`userId`,`createDate`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='销售订单表'
该表通过复合索引idx_user_date优化了按用户和时间范围的查询性能,status字段索引支持订单状态的高效筛选,确保在高并发场景下的查询响应速度。

核心功能实现深度解析
药品库存智能预警机制
系统实现了基于库存阈值的智能预警功能,当库存量低于安全库存时自动触发预警:
@Service
@Transactional
public class MedicineServiceImpl implements MedicineService {
@Autowired
private MedicineDao medicineDao;
@Autowired
private InventoryAlertDao alertDao;
@Override
public void checkInventoryAlert() {
List<Medicine> medicines = medicineDao.findAll();
for (Medicine medicine : medicines) {
// 计算安全库存阈值(基于近期销售数据)
int safeThreshold = calculateSafeThreshold(medicine.getId());
if (medicine.getNums() < safeThreshold) {
// 生成库存预警记录
InventoryAlert alert = new InventoryAlert();
alert.setMedicineId(medicine.getId());
alert.setMedicineName(medicine.getCName());
alert.setCurrentStock(medicine.getNums());
alert.setSafeThreshold(safeThreshold);
alert.setAlertTime(new Date());
alert.setStatus(0); // 未处理
alertDao.save(alert);
}
}
}
private int calculateSafeThreshold(int medicineId) {
// 基于近30天销售数据计算安全库存
Double avgDailySales = medicineDao.getAverageDailySales(medicineId, 30);
return (int) (avgDailySales * 7); // 保持7天销量作为安全库存
}
}
采购入库的事务一致性保障
采购入库操作涉及多个数据表的更新,系统通过Spring声明式事务确保数据一致性:
@Service
@Transactional
public class PurchaseServiceImpl implements PurchaseService {
@Autowired
private InOrderDao inOrderDao;
@Autowired
private MedicineDao medicineDao;
@Autowired
private InventoryDao inventoryDao;
@Override
public void processInOrder(InOrder inOrder) {
// 1. 创建入库订单
inOrder.setCreateDate(new Date());
inOrder.setStatus(1); // 已入库
inOrderDao.save(inOrder);
// 2. 更新药品库存
Medicine medicine = medicineDao.findByCName(inOrder.getCName());
if (medicine != null) {
medicine.setNums(medicine.getNums() + inOrder.getNums());
medicineDao.update(medicine);
} else {
// 新药品,创建记录
medicine = new Medicine();
medicine.setCName(inOrder.getCName());
medicine.setEName(inOrder.getEName());
medicine.setNums(inOrder.getNums());
medicine.setPrice(inOrder.getPrice());
medicineDao.save(medicine);
}
// 3. 记录库存变动
InventoryRecord record = new InventoryRecord();
record.setMedicineId(medicine.getId());
record.setChangeType(1); // 入库
record.setChangeAmount(inOrder.getNums());
record.setRemainingStock(medicine.getNums());
record.setCreateTime(new Date());
inventoryDao.saveRecord(record);
}
}

销售出库的库存实时扣减
销售出库功能实现了库存的实时扣减和销售记录的生成:
@Controller
@RequestMapping("/sell")
public class SellOrderController {
@Autowired
private SellOrderService sellOrderService;
@RequestMapping("/create")
@ResponseBody
public Map<String, Object> createSellOrder(@RequestBody SellOrder order) {
Map<String, Object> result = new HashMap<>();
try {
// 验证库存是否充足
Medicine medicine = medicineDao.findByCName(order.getCName());
if (medicine == null || medicine.getNums() < order.getBuyNums()) {
result.put("success", false);
result.put("message", "库存不足");
return result;
}
// 处理销售订单
sellOrderService.processSellOrder(order);
result.put("success", true);
result.put("message", "销售成功");
} catch (Exception e) {
result.put("success", false);
result.put("message", "销售失败:" + e.getMessage());
}
return result;
}
}
实体模型设计精要
系统采用面向对象的实体模型设计,每个实体类都对应数据库中的表结构:
@Entity
@Table(name = "t_medicine")
public class Medicine {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "cName", nullable = false, length = 20)
private String cName;
@Column(name = "eName", nullable = false, length = 20)
private String eName;
@Column(name = "price", nullable = false, precision = 11, scale = 2)
private Double price;
@Column(name = "nums", nullable = false)
private Integer nums;
@Column(name = "manufacturer", nullable = false, length = 30)
private String manufacturer;
// 其他字段及getter/setter方法
}
数据访问层采用MyBatis的Mapper接口方式,实现灵活的SQL映射:
@Repository
public interface MedicineDao {
/**
* 根据药品名称查询
*/
@Select("SELECT * FROM t_medicine WHERE cName = #{cName}")
Medicine findByCName(String cName);
/**
* 分页查询药品信息
*/
@Select("SELECT * FROM t_medicine ORDER BY id DESC LIMIT #{start}, #{size}")
List<Medicine> findByPage(@Param("start") int start, @Param("size") int size);
/**
* 更新药品库存
*/
@Update("UPDATE t_medicine SET nums = #{nums} WHERE id = #{id}")
int updateStock(@Param("id") int id, @Param("nums") int nums);
/**
* 根据类型查询药品
*/
@Select("<script>" +
"SELECT * FROM t_medicine WHERE 1=1" +
"<if test='typeId != null'> AND typeId = #{typeId}</if>" +
"<if test='keyword != null'> AND (cName LIKE CONCAT('%',#{keyword},'%') OR eName LIKE CONCAT('%',#{keyword},'%'))</if>" +
"</script>")
List<Medicine> findByCondition(@Param("typeId") Integer typeId, @Param("keyword") String keyword);
}

功能展望与优化方向
1. 引入Redis缓存提升系统性能
当前系统在高并发查询场景下可能存在性能瓶颈,建议引入Redis作为缓存层:
@Service
public class MedicineServiceWithCache {
@Autowired
private MedicineDao medicineDao;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String MEDICINE_CACHE_KEY = "medicine:";
private static final long CACHE_EXPIRE_TIME = 3600; // 1小时
public Medicine findByIdWithCache(Integer id) {
String cacheKey = MEDICINE_CACHE_KEY + id;
Medicine medicine = (Medicine) redisTemplate.opsForValue().get(cacheKey);
if (medicine == null) {
medicine = medicineDao.findById(id);
if (medicine != null) {
redisTemplate.opsForValue().set(cacheKey, medicine, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
}
}
return medicine;
}
}
2. 实现微服务架构改造
将单体应用拆分为微服务架构,提升系统的可扩展性和维护性:
- 药品服务:负责药品信息的CRUD操作
- 库存服务:处理库存管理和预警功能
- 订单服务:管理采购和销售订单
- 用户服务:处理用户认证和权限管理
3. 增加移动端适配和PWA支持
开发移动端应用,支持扫码入库、移动盘点等功能:
// 移动端药品扫码功能
class MedicineScanner {
async scanBarcode() {
try {
const result = await BarcodeScanner.scan();
const medicine = await this.getMedicineByBarcode(result.text);
this.displayMedicineInfo(medicine);
} catch (error) {
console.error('扫码失败:', error);
}
}
async getMedicineByBarcode(barcode) {
const response = await fetch(`/api/medicines/barcode/${barcode}`);
return await response.json();
}
}
4. 引入消息队列处理异步任务
使用RabbitMQ或Kafka处理库存同步、报表生成等异步任务:
@Component
public class InventoryMessageProducer {
@Autowired
private AmqpTemplate rabbitTemplate;
public void sendInventoryAlert(InventoryAlert alert) {
rabbitTemplate.convertAndSend("inventory.exchange", "alert.routingKey", alert);
}
}
@Component
public class InventoryMessageConsumer {
@RabbitListener(queues = "inventory.alert.queue")
public void processAlert(InventoryAlert alert) {
// 处理库存预警,如发送邮件通知
emailService.sendAlertEmail(alert);
}
}
5. 增强数据分析和预测功能
利用机器学习算法实现销售预测和智能补货建议:
# 销售预测模型示例
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
class SalesPredictor:
def train_model(self, historical_data):
# 特征工程:日期、季节、促销活动等
features = self.extract_features(historical_data)
model = RandomForestRegressor()
model.fit(features, historical_data['sales'])
return model
def predict_sales(self, model, future_periods):
predictions = model.predict(future_periods)
return predictions

总结
医药供应链智能管理平台通过SSM框架的有机结合,构建了一个稳定高效的药品进销存管理系统。系统在数据库设计上充分考虑了医药行业的特殊需求,在功能实现上确保了业务逻辑的完整性和数据的一致性。通过库存预警、事务管理、权限控制等核心功能的深度实现,为医药企业提供了全面的数字化管理解决方案。
未来的优化方向包括性能提升的缓存机制、架构升级的微服务改造、业务扩展的移动端支持等,这些改进将进一步提升系统的可用性和竞争力。该平台的成功实践为传统医药行业的数字化转型提供了可靠的技术支撑,具有良好的推广应用价值。