1. 项目概述
这个药店管理系统采用当前主流的前后端分离架构,使用Java SpringBoot作为后端框架,Vue3作为前端框架,MyBatis作为持久层框架,MySQL作为数据库。系统实现了药品进销存管理、会员管理、销售统计等核心功能,适合中小型药店日常经营管理使用。
我在实际开发过程中发现,这种技术组合特别适合需要快速开发又要求系统稳定的商业项目。SpringBoot提供了便捷的后端开发体验,Vue3的响应式特性让前端开发效率大幅提升,而MyBatis+MySQL的组合则保证了数据处理的可靠性和性能。
2. 技术架构解析
2.1 前后端分离架构设计
这个系统采用了典型的前后端分离架构,前端和后端完全独立开发部署。前端通过RESTful API与后端交互,后端只负责数据处理和业务逻辑,前端负责页面展示和用户交互。
这种架构的优势在于:
- 前后端可以并行开发,提高开发效率
- 前端可以使用更专业的技术栈,提升用户体验
- 后端API可以被多种客户端复用(Web、App等)
- 系统更容易扩展和维护
在实际开发中,我们使用Swagger来定义和测试API接口,确保前后端对接顺畅。接口文档自动生成,大大减少了沟通成本。
2.2 后端技术选型
后端采用SpringBoot框架,主要基于以下考虑:
- 快速开发:SpringBoot的自动配置和起步依赖让项目搭建变得非常简单
- 生态丰富:Spring生态有大量成熟的解决方案可供选择
- 性能稳定:经过大量企业级应用验证,可靠性有保障
我们特别使用了Spring Security来实现系统的权限控制,采用JWT(JSON Web Token)进行身份认证。这种无状态的认证方式特别适合前后端分离架构。
2.3 前端技术选型
前端选择Vue3主要因为:
- 响应式编程模型让开发更高效
- 组合式API让代码组织更灵活
- 性能优于Vue2,特别适合数据密集型的后台管理系统
- 丰富的生态系统(Element Plus、Vue Router等)
在实际开发中,我们使用Vuex进行状态管理,Element Plus作为UI组件库,axios处理HTTP请求。这种组合让前端开发既高效又规范。
3. 数据库设计
3.1 核心表结构
系统的主要数据库表包括:
-
药品表(medicine)
- id: 主键
- name: 药品名称
- specification: 规格
- manufacturer: 生产厂家
- price: 单价
- stock: 库存量
- category_id: 分类ID
-
会员表(member)
- id: 主键
- name: 姓名
- phone: 电话
- points: 积分
- level: 会员等级
-
销售单表(sale_order)
- id: 主键
- order_no: 订单编号
- member_id: 会员ID
- total_amount: 总金额
- payment: 实收金额
- create_time: 创建时间
-
销售明细表(sale_item)
- id: 主键
- order_id: 订单ID
- medicine_id: 药品ID
- quantity: 数量
- price: 单价
- amount: 金额
3.2 数据库优化
为了提高系统性能,我们做了以下数据库优化:
- 为常用查询字段建立索引
- 使用InnoDB引擎支持事务
- 合理设计表关系,避免过度规范化
- 对大表进行分表处理
- 使用连接池管理数据库连接
4. 核心功能实现
4.1 药品管理模块
药品管理模块实现了药品的CRUD操作,包括:
- 药品信息的添加、修改、删除
- 药品库存管理
- 药品分类管理
- 药品查询(支持多条件组合查询)
后端实现关键代码:
java复制@RestController
@RequestMapping("/api/medicine")
public class MedicineController {
@Autowired
private MedicineService medicineService;
@GetMapping
public ResponseEntity<List<Medicine>> listMedicines(
@RequestParam(required = false) String name,
@RequestParam(required = false) String category) {
List<Medicine> medicines = medicineService.findByConditions(name, category);
return ResponseEntity.ok(medicines);
}
@PostMapping
public ResponseEntity<Medicine> addMedicine(@RequestBody Medicine medicine) {
Medicine saved = medicineService.save(medicine);
return ResponseEntity.ok(saved);
}
// 其他CRUD方法...
}
前端实现关键代码:
javascript复制// 药品列表查询
const fetchMedicines = async () => {
loading.value = true
try {
const params = {
name: searchForm.name,
category: searchForm.category
}
const res = await getMedicines(params)
medicineList.value = res.data
} finally {
loading.value = false
}
}
4.2 销售管理模块
销售管理是系统的核心功能,主要流程包括:
- 创建销售单
- 添加销售明细
- 计算总金额
- 会员折扣计算
- 完成支付
- 更新库存
后端关键实现:
java复制@Transactional
public SaleOrder createOrder(SaleOrderDTO orderDTO) {
// 1. 创建订单
SaleOrder order = new SaleOrder();
order.setOrderNo(generateOrderNo());
order.setMemberId(orderDTO.getMemberId());
order.setCreateTime(new Date());
// 2. 处理订单明细
List<SaleItem> items = new ArrayList<>();
BigDecimal totalAmount = BigDecimal.ZERO;
for (SaleItemDTO itemDTO : orderDTO.getItems()) {
Medicine medicine = medicineRepository.findById(itemDTO.getMedicineId())
.orElseThrow(() -> new BusinessException("药品不存在"));
// 检查库存
if (medicine.getStock() < itemDTO.getQuantity()) {
throw new BusinessException(medicine.getName() + "库存不足");
}
// 扣减库存
medicine.setStock(medicine.getStock() - itemDTO.getQuantity());
medicineRepository.save(medicine);
// 计算明细金额
BigDecimal amount = medicine.getPrice().multiply(
new BigDecimal(itemDTO.getQuantity()));
SaleItem item = new SaleItem();
item.setOrder(order);
item.setMedicineId(medicine.getId());
item.setQuantity(itemDTO.getQuantity());
item.setPrice(medicine.getPrice());
item.setAmount(amount);
items.add(item);
totalAmount = totalAmount.add(amount);
}
// 3. 计算会员折扣
if (orderDTO.getMemberId() != null) {
Member member = memberRepository.findById(orderDTO.getMemberId())
.orElseThrow(() -> new BusinessException("会员不存在"));
BigDecimal discount = calculateDiscount(member.getLevel());
totalAmount = totalAmount.multiply(discount);
}
order.setTotalAmount(totalAmount);
order.setPayment(totalAmount); // 简化处理,实际支付金额可能不同
order.setItems(items);
return orderRepository.save(order);
}
4.3 库存预警功能
系统实现了库存预警功能,当药品库存低于设定阈值时,系统会发出预警。实现方式包括:
- 数据库层面设置库存阈值字段
- 定时任务检查库存状态
- 前端界面用不同颜色标识库存状态
关键实现代码:
java复制// 定时检查库存
@Scheduled(cron = "0 0 9 * * ?") // 每天上午9点执行
public void checkStock() {
List<Medicine> lowStockMedicines = medicineRepository.findByStockLessThanThreshold();
if (!lowStockMedicines.isEmpty()) {
// 发送预警通知
notificationService.sendStockWarning(lowStockMedicines);
}
}
5. 系统部署方案
5.1 开发环境配置
-
后端开发环境:
- JDK 11+
- Maven 3.6+
- IntelliJ IDEA或Eclipse
- MySQL 8.0+
-
前端开发环境:
- Node.js 14+
- npm或yarn
- VS Code或WebStorm
5.2 生产环境部署
系统支持多种部署方式:
-
传统部署:
- 后端打包为jar文件,使用java -jar命令运行
- 前端构建后部署到Nginx
- MySQL单独部署
-
Docker部署:
dockerfile复制# 后端Dockerfile示例 FROM openjdk:11-jre ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar"] -
云原生部署:
- 使用Kubernetes管理容器
- 配置自动扩缩容
- 使用云数据库服务
6. 常见问题与解决方案
6.1 性能优化问题
在实际运行中,我们遇到并解决了以下性能问题:
-
药品列表查询慢:
- 问题:当药品数据量达到万级时,列表查询响应变慢
- 解决方案:
- 添加适当的数据库索引
- 实现分页查询
- 使用缓存热门数据
-
销售高峰期系统响应延迟:
- 问题:促销活动时并发销售操作导致系统变慢
- 解决方案:
- 优化数据库事务隔离级别
- 引入消息队列异步处理非核心业务
- 增加服务器资源
6.2 数据一致性问题
在分布式环境下,我们遇到了以下数据一致性问题:
- 库存超卖问题:
- 现象:高并发下可能出现库存扣减为负数
- 解决方案:
- 使用数据库乐观锁
- 实现分布式锁
- 引入库存预占机制
关键代码实现:
java复制// 使用乐观锁解决超卖
@Transactional
public boolean reduceStock(Long medicineId, int quantity) {
Medicine medicine = medicineRepository.findById(medicineId)
.orElseThrow(() -> new BusinessException("药品不存在"));
if (medicine.getStock() < quantity) {
return false;
}
int rows = medicineRepository.reduceStockWithVersion(
medicineId, quantity, medicine.getVersion());
return rows > 0;
}
6.3 安全性问题
系统安全性方面的考虑和解决方案:
-
SQL注入防护:
- 使用MyBatis的参数绑定
- 避免拼接SQL语句
- 使用ORM框架的安全查询方法
-
XSS攻击防护:
- 前端使用vue-html-clean插件过滤HTML
- 后端对用户输入进行校验和转义
-
CSRF防护:
- 使用Spring Security的CSRF保护
- 前后端配合实现Token验证
7. 扩展功能建议
基于现有系统,可以考虑以下扩展方向:
-
移动端应用:
- 开发基于React Native的移动应用
- 实现扫码购药、会员卡电子化等功能
-
数据分析模块:
- 集成大数据分析工具
- 实现销售预测、库存优化等智能功能
-
供应链管理:
- 扩展供应商管理功能
- 实现自动补货、采购建议等
-
会员营销系统:
- 积分兑换功能
- 促销活动管理
- 会员精准营销
在实际开发中,我发现Vue3的Composition API特别适合这种复杂业务系统的开发,它让代码组织更加清晰,逻辑复用更加方便。而SpringBoot的自动配置特性则大大减少了后端开发的样板代码,让我们能更专注于业务逻辑的实现。