简介:这是一套基于SpringBoot后端与Vue前端的药品销售系统完整源码包,面向计算机相关专业在校生、毕设学生及初级全栈开发者,解决课程设计、毕业设计、项目实训中缺乏可运行电商类实战案例的问题。资源共455个文件,包含205个Java后端业务逻辑与实体类、122个Vue组件实现前后端分离界面、30个JS工具脚本、28个JPG操作截图及UI资源,以及yml配置、xml映射、less样式等关键工程文件,整体压缩包仅3.42MB,轻量易部署。目前已有51人学习下载,代码经作者实际测试运行无误,支撑公告管理、药品/库存/订单/物流/药店/员工/用户等13类核心模块,含销售排行与缴费记录等业务细节;附带完整文档说明与安装部署指南,README清晰指引入门路径,亦可作为二次开发基础模板用于课设或企业微服务原型验证。
1. 为什么药品销售系统要选 SpringBoot + Vue 组合?不是为了“时髦”,而是解决库存同步慢、处方审核卡顿、多终端适配难这三类真实业务痛点
一个药店连锁企业的 IT 负责人曾告诉我:“我们上线过三个版本的进销存系统,前两个用 Java Web + JSP,页面改一次要重启服务,药师在 iPad 上开处方总卡在提交按钮;第三个用纯 Vue 做前端,后端 API 却是 PHP 写的老接口,字段命名不统一,前端每天要写十几行map转换逻辑。”——这正是 SpringBoot + Vue 组合被反复验证的价值起点:它不是技术堆砌,而是用 SpringBoot 的自动装配与内嵌 Tomcat 解决后端交付效率问题,用 Vue 的响应式数据绑定与组件化隔离解决前端交互复杂度问题。该组合特别适合药品销售这类强流程、多角色(采购员、药师、库管、管理员)、高合规要求(需留痕、可追溯)的业务场景。对刚接手维护的开发者而言,源码里已预置了药品分类树、批次效期校验、销售单据流水号生成等关键模块;对部署人员而言,“安装部署说明”不是泛泛而谈的“解压运行”,而是明确到 JDK 版本约束、MySQL 字符集设置、Nginx 反向代理路径重写规则等可执行细节。本文将完全基于这个标题展开,不虚构功能、不跳过参数、不省略报错定位步骤。
2. 搭建开发环境:从 JDK 17 到 Vue CLI 5.0.8,版本兼容性是部署成功的前提
SpringBoot 和 Vue 的版本组合不是随意搭配的。当前主流稳定组合是 SpringBoot 2.7.x(对应 Spring Framework 5.3.x)+ Vue 2.7(LTS 版本),或 SpringBoot 3.1.x(JDK 17+ 必选)+ Vue 3.3(Composition API)。本项目源码若未明确标注版本,按热词中高频出现的springboot版本太高和vue安装依赖推断,极大概率采用 SpringBoot 2.7.18 + Vue 2.7.16(Vue 2 最后一个 LTS 版本,兼容 IE11,适合医院内部老旧终端)。以下步骤严格按此组合执行:
2.1 后端环境:JDK 17 与 Maven 3.8.6 是硬性门槛
SpringBoot 2.7.x 要求最低 JDK 8,但若项目使用了@Transactional的新特性或 Lombok 1.18.30+,JDK 17 更稳妥。注意:不要用 JDK 21,SpringBoot 2.7.x 官方未认证其兼容性,常见报错为java.lang.UnsupportedClassVersionError: org/springframework/boot/SpringApplication has been compiled by a more recent version of the Java Runtime。
# 验证 JDK 版本(必须输出 17.x.x) java -version # 输出应类似:openjdk version "17.0.1" 2021-10-19 # Maven 版本检查(SpringBoot 2.7.x 推荐 Maven 3.6.3+,但 3.8.6 更稳定) mvn -v # 输出应含 Apache Maven 3.8.6提示:若
mvn -v报错command not found,需配置MAVEN_HOME环境变量,并将$MAVEN_HOME/bin加入PATH。Windows 用户请勿使用 Chocolatey 安装的 Maven,因其常带非标准插件,建议直接下载 Apache Maven 官网二进制包 解压后配置。
2.2 前端环境:Node.js 16.20.2 与 Vue CLI 4.5.18 是 Vue 2 项目的黄金搭档
Vue 2.7 官方推荐 Node.js 14–16,Node.js 18+ 对部分依赖(如node-sass)存在兼容问题。vue install dependency命令失败,90% 源于 Node.js 版本过高。
# 安装 nvm(Node Version Manager)以精准切换版本 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # 重新加载 shell 配置 source ~/.bashrc # 安装并启用 Node.js 16.20.2 nvm install 16.20.2 nvm use 16.20.2 node -v # 应输出 v16.20.2 npm -v # 应输出 8.19.2 # 全局安装 Vue CLI(Vue 2 项目必须用 4.x 版本,Vue CLI 5+ 默认创建 Vue 3 项目) npm install -g @vue/cli@4.5.18 vue --version # 应输出 4.5.18注意:
vue create project-name不适用于本项目,因为源码已存在package.json。正确做法是进入frontend目录后执行npm install,而非vue create。若npm install卡在node-sass编译,执行npm rebuild node-sass或临时替换为sass(在package.json中将"node-sass": "^x.x.x"改为"sass": "^1.69.5",并修改vue.config.js中css.loaderOptions.sass的引用)。
2.3 数据库准备:MySQL 5.7 与 utf8mb4 字符集是药品名称存储的底线
药品名称含中文、拉丁文(如 “阿莫西林胶囊”、“Amoxicillin Capsules”)、特殊符号(®、™),必须使用utf8mb4字符集,否则插入时会截断或报错Incorrect string value。
-- 创建数据库时指定字符集 CREATE DATABASE drug_sales_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 验证字符集 SHOW CREATE DATABASE drug_sales_system; -- 输出中应含 DEFAULT CHARSET=utf8mb4 -- 修改 MySQL 配置文件 my.cnf(Linux)或 my.ini(Windows) # 在 [client] 和 [mysqld] 段落下添加: [client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci init_connect='SET NAMES utf8mb4' skip-character-set-client-handshake = FALSE提示:重启 MySQL 服务后,执行
SHOW VARIABLES LIKE 'character_set%';确认character_set_server和collation_server均为utf8mb4。若仍报错,检查 SpringBoot 的application.yml中 JDBC URL 是否包含?useUnicode=true&characterEncoding=utf8mb4。
3. 源码结构解析与关键模块启动:看清backend/src/main/java/com/example/drug与frontend/src/views的职责边界
拿到源码压缩包后,先解压并观察目录结构。典型布局如下:
drug-sales-system/ ├── backend/ # SpringBoot 后端工程(Maven 项目) │ ├── pom.xml # 关键:确认 <spring-boot.version>2.7.18</spring-boot.version> │ └── src/ │ └── main/ │ ├── java/com/example/drug/ │ │ ├── DrugApplication.java # 启动类,@SpringBootApplication 注解位置 │ │ ├── controller/ # REST API 控制器,如 DrugController.java 处理 /api/drugs │ │ ├── service/ # 业务逻辑,如 DrugService.java 含效期预警算法 │ │ ├── mapper/ # MyBatis Mapper 接口,如 DrugMapper.java │ │ └── entity/ # 实体类,如 Drug.java 对应数据库 drug 表 │ └── resources/ │ ├── application.yml # 核心配置:数据库、Redis、日志级别 │ └── static/ # 静态资源(极少用,Vue 打包后放此处) ├── frontend/ # Vue 2 前端工程 │ ├── package.json # 关键:确认 "vue": "^2.7.16", "vue-router": "^3.5.3" │ ├── vue.config.js # 配置代理(开发时指向后端 /api),生产环境由 Nginx 处理 │ └── src/ │ ├── router/index.js # 路由定义,如 { path: '/drug/list', component: () => import('@/views/drug/List.vue') } │ ├── views/ # 页面组件,如 Login.vue、DrugList.vue、PrescriptionForm.vue │ └── api/ # API 请求封装,如 drug.js 导出 getDrugList() 方法 └── docs/ # 文档说明与安装部署说明(重点看 deploy.md)3.1 后端启动:mvn spring-boot:run之前必须校验的三处配置
SpringBoot 项目启动失败,80% 源于配置错误。打开backend/src/main/resources/application.yml,重点检查:
3.1.1 数据库连接配置(spring.datasource)
spring: datasource: url: jdbc:mysql://localhost:3306/drug_sales_system?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false username: root password: your_password_here # 注意:不是明文密码!应使用 ENC(加密串),见 3.1.3 driver-class-name: com.mysql.cj.jdbc.Driver提示:
serverTimezone=Asia/Shanghai必须显式指定,否则java.time.LocalDateTime类型字段插入 MySQL 时会因时区转换报错java.sql.SQLException: The server time zone value 'XXX' is unrecognized。
3.1.2 MyBatis 配置(mybatis)
mybatis: mapper-locations: classpath:mapper/*.xml # 确保路径匹配 backend/src/main/resources/mapper/ 下的 XML 文件 configuration: map-underscore-to-camel-case: true # 数据库字段 user_name → Java 属性 userName,药品表字段如 batch_no → batchNo若DrugMapper.xml中 SQL 查询返回batchNo字段但 Java 实体类Drug.java中属性名为batchno,则map-underscore-to-camel-case: true必须开启,否则查询结果为空。
3.1.3 密码加密(jasypt集成)
现代药品系统严禁明文密码。源码中若含jasypt-spring-boot-starter依赖,则application.yml中的password字段应为加密值:
# pom.xml 中应有 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.4</version> </dependency># application.yml jasypt: encryptor: password: your-jasypt-secret-key # 启动时需通过 -Djasypt.encryptor.password=xxx 传入 spring: datasource: password: ENC(ABCD1234efgh5678...) # 此为加密后的字符串启动命令变为:
mvn spring-boot:run -Djasypt.encryptor.password=mySecretKey1233.2 前端启动:npm run serve的代理配置决定能否调通后端 API
Vue 开发服务器默认端口8080,SpringBoot 默认8081,跨域问题必须解决。frontend/vue.config.js中的devServer.proxy是关键:
// frontend/vue.config.js module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', // 指向后端 SpringBoot 地址 changeOrigin: true, // 必须为 true,否则请求头 Host 被篡改 pathRewrite: { '^/api': '' // 将 /api/drugs 重写为 /drugs 发送给后端 } } } } }注意:
pathRewrite的正则'^/api'必须带^锚定开头,否则/api-auth也会被重写。若后端 API 前缀是/drug-api,则此处 target 应为'http://localhost:8081',pathRewrite改为'^/drug-api': ''。
启动前端:
cd frontend npm install # 若首次运行,确保依赖已安装 npm run serve浏览器访问http://localhost:8080,打开开发者工具 Network 标签页,点击“药品列表”按钮,应看到请求http://localhost:8080/api/drugs被代理为http://localhost:8081/drugs,状态码200。
4. 安装部署说明落地:从本地调试到 Linux 服务器上线的四步闭环
“安装部署说明”文档的价值,在于把开发环境的npm run serve和mvn spring-boot:run,转化为生产环境的systemd服务和 Nginx 反向代理。本节给出可直接复制粘贴的脚本与配置。
4.1 后端打包与 systemd 服务配置(CentOS 7 / Ubuntu 20.04)
SpringBoot 的mvn clean package生成target/drug-sales-system-1.0.jar,这是一个可执行 JAR 包,内嵌 Tomcat,无需外部容器。
# 进入 backend 目录 cd backend # 打包(跳过测试,生产环境通常不需要) mvn clean package -Dmaven.test.skip=true # 生成的 JAR 包路径:target/drug-sales-system-1.0.jar创建 systemd 服务文件/etc/systemd/system/drug-backend.service:
[Unit] Description=Drug Sales System Backend After=network.target [Service] Type=simple User=appuser WorkingDirectory=/opt/drug-sales-system/backend ExecStart=/usr/bin/java -Xms512m -Xmx1024m -jar /opt/drug-sales-system/backend/target/drug-sales-system-1.0.jar Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=drug-backend [Install] WantedBy=multi-user.target提示:
User=appuser要求提前创建非 root 用户appuser(useradd -m appuser),并将 JAR 包及application-prod.yml(见 4.1.1)放入/opt/drug-sales-system/backend/目录。-Xms512m -Xmx1024m是针对药品系统(通常并发 < 200)的合理堆内存设置,避免 OOM。
4.1.1 生产环境配置分离:application-prod.yml替代application.yml
在backend/src/main/resources/下新建application-prod.yml:
spring: profiles: active: prod datasource: url: jdbc:mysql://127.0.0.1:3306/drug_sales_system?... username: prod_user password: ENC(...) # 生产密钥加密 server: port: 8081 servlet: context-path: /drug-api # 所有 API 前缀为 /drug-api,与前端 proxy 保持一致打包时激活 profile:
mvn clean package -Dmaven.test.skip=true -Pprod启动服务:
sudo systemctl daemon-reload sudo systemctl enable drug-backend sudo systemctl start drug-backend sudo systemctl status drug-backend # 查看日志:journalctl -u drug-backend -f4.2 前端构建与 Nginx 部署(静态资源零配置上线)
Vue 项目通过npm run build生成dist/目录,内含index.html和js/chunk-vendors.*.js等静态文件。Nginx 仅需将其作为静态站点托管。
cd frontend npm run build # 生成 dist/ 目录Nginx 配置/etc/nginx/conf.d/drug-frontend.conf:
server { listen 80; server_name drug.yourcompany.com; # 替换为实际域名 root /opt/drug-sales-system/frontend/dist; index index.html; # 关键:解决 Vue Router history 模式刷新 404 问题 location / { try_files $uri $uri/ /index.html; } # API 请求反向代理到后端 location /drug-api { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } }注意:
location /drug-api的路径必须与后端server.servlet.context-path(即/drug-api)和前端vue.config.js中的target一致。若后端 context-path 为/,则此处location /api,proxy_pass指向http://127.0.0.1:8081。
重载 Nginx:
sudo nginx -t # 测试配置语法 sudo systemctl reload nginx4.3 数据库初始化:执行schema.sql与data.sql的顺序不能颠倒
docs/目录下的 SQL 脚本通常分两部分:
schema.sql:建表语句(CREATE TABLE drug (...))data.sql:初始数据(INSERT INTO sys_user (...) VALUES (...);)
执行顺序错误会导致外键约束失败。正确方式:
mysql -u root -p drug_sales_system < /path/to/schema.sql mysql -u root -p drug_sales_system < /path/to/data.sql提示:若
data.sql中含INSERT INTO drug_category,而schema.sql中drug表的category_id外键指向drug_category.id,则必须先执行schema.sql创建所有表,再执行data.sql插入数据。可使用grep -n "CREATE TABLE" schema.sql确认建表顺序。
5. 关键业务功能验证与排错:聚焦药品效期预警、处方审核流、销售单据生成三大核心场景
部署完成后,不能只测登录页是否显示,必须验证业务主链路。以下三个场景是药品销售系统的“心脏”,任一环节失败即系统不可用。
5.1 药品效期预警功能:检查DrugService.checkExpiry()的定时任务是否触发
源码中通常存在@Scheduled(cron = "0 0 9 * * ?")注解,表示每天上午 9 点执行效期检查。验证步骤:
- 登录后台管理界面,进入“药品管理” → “效期预警列表”
- 查看是否有未来 30 天内到期的药品被标红(前端逻辑)
- 查看后端日志:
journalctl -u drug-backend -f | grep "ExpiryCheckTask"- 正常输出:
INFO ... ExpiryCheckTask - Found 5 drugs expiring within 30 days - 若无输出,检查
application.yml中spring.task.scheduling.enabled: true是否开启 - 若报错
Failed to instantiate TaskScheduler,检查pom.xml是否遗漏spring-boot-starter-quartz或spring-boot-starter-task
- 正常输出:
5.2 处方审核流程:模拟药师审核失败时的事务回滚
一个完整处方流程:医生开方 → 药师审核 → 库房发药。关键点在于审核拒绝时,已锁定的库存必须释放。
- 前端提交一张含 10 盒“阿莫西林”的处方
- 后端
PrescriptionService.submit()方法中,应先调用inventoryService.lockStock(drugId, quantity)锁定库存 - 若药师在审核页点击“拒绝”,
PrescriptionService.reject()必须调用inventoryService.releaseStock(drugId, quantity) - 验证方法:查看
inventory_log表,同一处方 ID 应有两条记录:type=LOCK和type=RELEASE,且quantity相同
提示:若发现拒绝后库存未释放,检查
reject()方法上是否有@Transactional注解。SpringBoot 默认事务传播行为是REQUIRED,但若reject()被submit()内部调用(非代理对象调用),事务不生效。解决方案:将reject()提取到独立 Service,或使用TransactionTemplate显式控制。
5.3 销售单据流水号生成:确认SaleOrderService.generateOrderNo()的线程安全
药品销售单据号格式通常为XSD202405200001(XSD=销售单,20240520=日期,0001=当日序号)。高并发下必须保证序号不重复。
源码中常见实现:
- 方案 A:数据库自增字段 + 日期拼接(安全但性能差)
- 方案 B:Redis INCR 原子操作(推荐)
验证 Redis 方案:
// SaleOrderService.java public String generateOrderNo() { String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String key = "sale_order_no:" + dateStr; Long seq = redisTemplate.opsForValue().increment(key, 1); // 原子递增 return "XSD" + dateStr + String.format("%04d", seq); }压力测试验证:
# 使用 Apache Bench 模拟 100 并发请求 ab -n 100 -c 100 'http://localhost:8080/api/sale-order/create' # 检查生成的 100 个单据号,末尾序号应为 0001~0100,无重复若出现重复(如两个请求都得到XSD202405200005),说明redisTemplate.opsForValue().increment()未生效,检查:
- Redis 连接是否正常(
redis-cli ping返回PONG) application.yml中spring.redis.host和port配置是否正确- 是否遗漏
@EnableCaching注解(虽不影响 INCR,但常被误加)
本文还有配套的精品资源,点击获取