1. 项目概述与核心价值
这个药品商城系统是一个典型的JavaWeb电商应用,采用SSM(Spring+SpringMVC+MyBatis)框架组合开发。作为JavaEE领域的经典实战项目,它完整覆盖了从数据库设计到前端展示的全流程技术栈。我在实际医疗行业信息化项目中多次迭代过类似系统,发现药品电商相比普通电商有三大特殊需求:严格的药品分类管理(处方药/非处方药)、复杂的审批流程、以及特殊的库存预警机制。
系统采用MVC分层架构,后端使用Spring 5.2.6管理Bean和事务,SpringMVC处理HTTP请求路由,MyBatis 3.5.6操作MySQL 8.0数据库。前端用JSP+JQuery实现动态页面,这种技术组合在传统企业级应用中仍占主流地位。特别值得注意的是,药品商城的数据库设计需要遵循GSP(药品经营质量管理规范)要求,这是很多教程不会提及的关键点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境搭建与关键技术选型
2.1 开发环境配置
推荐使用IntelliJ IDEA 2021.3 + Tomcat 9.0 + JDK 1.8的组合。与VSCode相比,IDEA对JavaEE项目的支持更完善,特别是对JSP的智能提示和热部署功能。MySQL建议使用8.0以上版本,需要特别注意character-set-server必须设置为utf8mb4,否则药品名称中的生僻字(如"羟氯喹")会出现乱码。
关键Maven依赖包括:
xml复制<!-- Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- MyBatis整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
2.2 为什么选择SSM框架
在2023年仍有新项目采用SSM而非Spring Boot,主要考虑两点:一是医院内部系统往往需要与老旧系统集成,SSM的XML配置方式更灵活;二是药品管理系统通常需要深度定制MyBatis的SQL执行过程(比如添加药品操作审计日志)。Spring的声明式事务管理对药品库存扣减这类操作至关重要,我们会在后面详细说明其配置要点。
3. 数据库设计与优化
3.1 药品核心表结构
sql复制CREATE TABLE `medicine` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '药品ID',
`name` varchar(100) NOT NULL COMMENT '通用名',
`spec` varchar(50) NOT NULL COMMENT '规格',
`approval_number` varchar(50) NOT NULL COMMENT '批准文号',
`category_id` int(11) NOT NULL COMMENT '分类(1:处方药 2:非处方药)',
`price` decimal(10,2) NOT NULL COMMENT '单价',
`stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(0:下架 1:上架)',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_approval` (`approval_number`) COMMENT '批准文号唯一索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='药品信息表';
药品表设计有四个关键点:1) 批准文号必须唯一索引 2) 价格使用DECIMAL而非FLOAT避免精度丢失 3) 分类字段需与国药准字编码规则对应 4) 库存字段需配合事务控制。
3.2 订单事务处理
药品订单涉及库存扣减,必须使用Spring事务管理:
java复制@Transactional(rollbackFor = Exception.class)
public void placeOrder(Order order) {
// 1. 检查库存
Medicine medicine = medicineMapper.selectForUpdate(order.getMedicineId());
if(medicine.getStock() < order.getQuantity()) {
throw new RuntimeException("库存不足");
}
// 2. 扣减库存
medicineMapper.updateStock(medicine.getId(),
medicine.getStock() - order.getQuantity());
// 3. 创建订单
orderMapper.insert(order);
}
这里的关键是selectForUpdate使用悲观锁,防止超卖。实际项目中我们还需要添加药品销售记录表,满足GSP要求的可追溯性。
4. 前端交互实现技巧
4.1 JSP与JQuery整合
在medicineList.jsp中实现药品搜索和分页:
jsp复制<script>
$(function(){
// 药品分类切换
$('.category-tab').click(function(){
let categoryId = $(this).data('id');
$.get('medicine/list', {categoryId: categoryId}, function(data){
$('#medicine-container').html(data);
});
});
// 加入购物车
$('.add-cart').click(function(){
let medicineId = $(this).data('id');
$.post('cart/add', {medicineId: medicineId}, function(res){
if(res.success) {
toastr.success('添加成功');
} else {
toastr.error(res.message);
}
});
});
});
</script>
4.2 药品图片处理技巧
药品图片需要特殊处理:
- 使用阿里云OSS存储药品包装盒照片
- JSP中显示缩略图:
jsp复制<img src="${medicine.imageUrl}?x-oss-process=image/resize,w_200"
alt="${medicine.name}"
onerror="this.src='images/default-medicine.png'">
- 处方药图片需添加水印:"凭医师处方销售"
5. 项目部署与性能优化
5.1 Tomcat调优参数
在conf/server.xml中配置:
xml复制<Connector port="8080" protocol="HTTP/1.1"
maxThreads="500"
minSpareThreads="50"
acceptCount="300"
compression="on"
URIEncoding="UTF-8"/>
药品商城的并发特点是大流量查询和小批量订单,因此需要增大maxThreads。同时启用GZIP压缩减少药品图片的传输体积。
5.2 MySQL性能优化
- 为药品表添加复合索引:
sql复制ALTER TABLE medicine ADD INDEX idx_search (category_id, status, stock);
- 配置连接池(推荐使用Druid):
properties复制# druid配置
spring.datasource.druid.initial-size=5
spring.datasource.druid.max-active=50
spring.datasource.druid.max-wait=60000
spring.datasource.druid.time-between-eviction-runs-millis=60000
6. 常见问题排查指南
6.1 药品模糊查询性能低
现象:搜索"阿莫西林"响应慢
解决方案:
- 添加全文索引:
sql复制ALTER TABLE medicine ADD FULLTEXT INDEX ft_name (name);
- 修改查询语句:
java复制@Select("SELECT * FROM medicine WHERE MATCH(name) AGAINST(#{keyword})")
List<Medicine> searchByKeyword(String keyword);
6.2 订单超卖问题
现象:并发下单时库存出现负数
终极解决方案:
sql复制UPDATE medicine SET stock = stock - #{quantity}
WHERE id = #{id} AND stock >= #{quantity}
配合MyBatis的update返回值判断是否成功:
java复制int rows = medicineMapper.updateStock(param);
if(rows == 0) {
throw new RuntimeException("库存不足");
}
7. 扩展功能建议
- 药品效期管理:在库存表中添加expire_date字段,并创建定时任务检查临期药品
- 处方审核流程:集成CA数字证书验证医师电子签名
- 医保对接:实现医保目录匹配和报销计算
- 温湿度监控:对接物联网设备记录药品存储环境数据
在实现SSM药品商城时,我最深刻的体会是:药品数据比普通商品更需要严谨性。比如药品名称必须使用国家药典委员会公布的通用名,不能使用商品名;价格变动需要保留历史记录以备检查;所有删除操作都应该转为状态变更。这些行业特性往往比技术实现更具挑战性。
