资讯动态

基于Java SSM与MySQL的自助购药小程序设计与实现

发布时间:2026/10/18 11:55:14 来源:尧图企业网站定制
简介这套自助购药小程序源码面向计算机相关专业毕业设计与课程设计场景基于Java、SSM框架和MySQL数据库构建B/S架构的在线购药系统覆盖首页、个人中心、用户管理、商家管理、药品信息管理、药品分类管理、发票信息管理、系统管理等核心模块完整呈现药品搜索、浏览、下单与管理后台的业务链路。压缩包共含1319个文件约22.55MB其中Java源码、Vue页面、JS逻辑、WXML/WXSS小程序文件构成完整前后端png/svg图片资源用于界面展示SQL数据库脚本、XML配置、Maven文件及批处理脚本保证了项目可导入可直接运行。环境说明覆盖JDK1.8、MySQL5.7、eclipse/idea、Tomcat7、微信开发者工具等部署链路清晰。已有57人学习下载适合需要参考完整购药流程、权限设计及前后端分离实现并希望快速搭建可演示项目的毕业设计或课程设计学习者。1. 从“买药难”入手这套自助购药小程序方案解决什么做毕设或课程设计的时候我见过不少同学把“药店购药小程序”当成一个简单的增删改查来写用户表、药品表、订单表再加两个页面就跑起来交差。真拿去答辩才发现商家入驻、发票管理、药品分类、用户权限这些模块全都没覆盖数据库建表也撑不住一点并发场景。这套基于 Java SSM MySQL 5.7 的自助购药小程序方案是从实际搭过的项目里整理出来的它不只是在“做出来”而是把前后端完整拆开小程序端负责搜索浏览和下单后端负责商家管理、药品维护和发票处理。适合正在做毕业设计/课程设计或者想拿一个完整 Java 后端项目练手的人参考。2. 需求分析与技术选型自助购药的业务链路和 SSM 组合2.1 先梳理角色和流程不是简单增删改查我在拿到“自助购药”这个题目时给自己定过一个要求不能只做订单的增删改查。药店的业务链路比普通小卖部长它要面向用户、商家和平台管理员三个角色而且每个角色看到的数据都不一样。用户打开小程序后看到的是药品分类、推荐药、搜索框和购物车商家登录后台之后看到的是自己的店铺信息、药品库存和待发货订单管理员则要看用户列表、商家入驻列表、药品上架状态和发票记录。如果需求分析阶段没把这些边界划清楚后面很容易出现“用户能改别人订单”这种低级问题。通常的做法是先列一个角色权限矩阵我一般会把每个模块按“查看、新增、修改、删除”拆开比如用户只能查看自己的订单和发票商家只能管理自己名下的药品管理员才能操作所有数据。这样一来Controller 层的每个接口都对应一个明确的角色权限答辩时也能直接说清楚“我的权限控制是怎么设计的”。2.2 技术选型为什么用 Java SSM MySQL 5.7项目用的是 Java 语言JDK 版本 1.8后端框架选择 SSM也就是 Spring、SpringMVC、MyBatis 的组合。这个选择不是因为它新而是因为它能把“配置过程”完整暴露出来Spring 管理 Service 层的 Bean 和事务SpringMVC 接收小程序发过来的请求并转发给 ControllerMyBatis 用 XML 的方式把 SQL 和 Java 方法做映射。对毕业设计/课程设计来说这套组合比一上来就用 Spring Boot 更有讲解空间答辩的时候可以从配置文件讲到核心原理不会被问几句就卡住。数据库用 MySQL 5.7配合 Navicat 11 进行导入和可视化操作项目里的数据库脚本文件就是针对 5.7 写的避免出现高版本驱动带来兼容性问题。Maven 3.3 负责统一管理依赖Tomcat 7 作为 Java Web 的部署容器。前端小程序部分在 HBuilderX 和微信开发者工具中打开两个工具的区别是HBuilderX 负责把源码跑起来微信开发者工具负责预览和真机调试。我一般会建议学生不要急着换技术栈先把这套组合跑通。原因在后面的避坑章节会展开SSM Tomcat 7 MySQL 5.7 是一套经过很多项目验证的稳定组合出问题都能在网上快速找到解决方案对交付时间紧张的项目来说稳定性比技术新颖更重要。2.3 功能模块拆解八个模块的边界这个系统的功能模块在摘要里写得很清楚主要包括首页、个人中心、用户管理、商家管理、药品信息管理、药品分类管理、发票信息管理、系统管理。我按实际开发顺序重新分组这样更容易对应到数据库表模块主要功能对应角色首页药品展示、搜索入口、分类快捷入口用户个人中心查看订单、编辑资料、发票申请用户用户管理用户列表、禁用/启用账号管理员商家管理商家入驻、店铺信息维护管理员、商家药品信息管理药品上下架、价格库存维护管理员、商家药品分类管理分类的增删改查管理员发票信息管理发票抬头、开票记录用户、管理员系统管理轮播图、公告等配置管理员这样拆完之后你会发现订单没有被单独列成一个“订单管理”因为订单是在用户购药流程中自然产生的数据它分散在个人中心和商家端。后续实现的时候我再单独建一张订单表而不是在模块清单里生硬地加一个“订单管理”模块。3. 总体架构设计B/S 三层结构、数据库表与项目目录划分3.1 B/S 架构中的数据流转整个系统采用 B/S 机构。很多人把 B/S 理解成“用浏览器访问”其实对小程序的开发来说B/S 依然成立小程序端负责展示和交互业务逻辑全部放在 Web 服务器Tomcat 7上。数据流转路径是这样的小程序端发起 wx.request 请求 → SpringMVC 的 DispatcherServlet 接收 URL → Controller 层做参数包装 → Service 层处理业务规则 → DAO/Mapper 层操作数据库 → 返回 JSON 给小程序渲染这套流程的关键点在于小程序端只负责拿到 JSON 并渲染页面所有需要权限判断的地方都放在后端 Service 层。我在做方案时最担心的是前端把不需要的信息全部显示出来比如药品库存、商家电话这些字段都返回到小程序上既影响加载速度也暴露不必要的信息。合理的做法是在 Controller 直接组装 ViewObject只返回当前页面需要的字段。3.2 数据库设计六张核心表与外键关系我通常会根据模块清单先建六张核心表用户表 user、商家表 merchant、药品表 medicine、分类表 medicine_category、订单表 orders、订单明细表 order_detail。发票信息可以单独做成 invoice 表也可以通过订单号关联为了满足发票信息管理模块我会单独建一张发票表。下面以药品表为例展示一个可以直接用到项目里的建表脚本CREATE TABLE medicine ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT 药品名称, category_id int(11) DEFAULT NULL COMMENT 药品分类id, merchant_id int(11) DEFAULT NULL COMMENT 商家id, price decimal(10,2) NOT NULL COMMENT 单价, stock int(11) NOT NULL DEFAULT 0 COMMENT 库存, image varchar(255) DEFAULT NULL COMMENT 药品图片, prescription_flag tinyint(1) DEFAULT 0 COMMENT 1处方药 0非处方药, status tinyint(1) DEFAULT 1 COMMENT 1上架 0下架, create_time datetime DEFAULT NULL COMMENT 上架时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;这张表放在中间层的设计原因medicine 通过 category_id 关联药品分类通过 merchant_id 关联商家这样商家登录后台后可以只维护自己的药品管理员可以维护全部。stock 字段在订单模块里承担库存校验的作用后面下单接口会用到。其余的关联关系orders 表保存 user_id 和总金额order_detail 表保存 order_id 和 medicine_idinvoice 表通过 order_id 关联一笔订单避免重复开票。所有表都用 InnoDB 引擎外键不一定会按数据库约束去建因为实际项目中更多是在 Service 层保证一致性这点答辩时如果被问到“为什么不建外键”可以这样回答减少无效约束开销逻辑外键由代码控制。3.3 后端目录结构Controller、Service、Dao 分层拿到一份项目源码我习惯先看目录目录结构决定后面排查问题的效率。这套项目后端按标准 SSM 三层结构组织src/main/java/com/example/pharmacy ├── controller/ # 请求入口返回 JSON 数据 │ ├── MedicineController.java │ ├── UserController.java │ ├── OrderController.java │ └── InvoiceController.java ├── service/ # 业务逻辑接口和实现类 │ ├── MedicineService.java │ ├── impl/ │ └── ... ├── dao/ # MyBatis Mapper 接口 │ ├── MedicineMapper.java │ └── ... ├── entity/ # 数据库实体类 │ ├── Medicine.java │ └── ... └── common/ # 统一返回结果 Result、分页工具resources 目录下面通常会有 mappers 文件夹存放各实体对应的 XML 文件还有 spring 和 springmvc 两个配置文件。我把 dao 层叫作 dao 而不是 mapper是因为在 SSM 里它既要承担 Mapper 接口的定义也持有 MyBatis 生成的代理实现。改代码的时候Controller 只负责参数传递不写 SQLSQL 只出现在 mappers XML 里面。这样职责边界清楚出问题也好定位。4. 核心模块实现药品管理、订单接口与小程序的联调编码4.1 小程序端首页和分类切换小程序端我用原生框架开发。首页最重要的部分是商品列表它会请求后端/medicine/list接口带分页参数和分类参数。下面这段代码是下拉刷新的核心逻辑Page({ data: { list: [], page: 1, size: 10, categoryId: 0, hasMore: true, loading: false }, loadMedicines() { if (this.data.loading || !this.data.hasMore) return; this.setData({ loading: true }); const that this; wx.request({ url: http://localhost:8080/medicine/list, data: { page: that.data.page, size: that.data.size, categoryId: that.data.categoryId, keyword: that.data.keyword || }, success(res) { const data res.data.data; const list that.data.list.concat(data.list); that.setData({ list: list, page: that.data.page 1, hasMore: data.list.length that.data.size, loading: false }); } }); }, onPullDownRefresh() { this.setData({ list: [], page: 1, hasMore: true }); this.loadMedicines(); wx.stopPullDownRefresh(); } });这里有两个细节一是用hasMore阻止空请求二是把page放在 data 里而不是全局变量方便下拉刷新时重置。小程序端调试时url可以直接写localhost前提是开发者工具里勾选“不校验合法域名”。4.2 后端药品列表接口从 Controller 到 MyBatis后端对应的小程序接口我一般会用ResponseBody返回一个统一的 Result 对象结构是{ code, message, data }。代码写出来大概是Controller RequestMapping(/medicine) public class MedicineController { Autowired private MedicineService medicineService; RequestMapping(/list) ResponseBody public Result list( RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Integer categoryId, RequestParam(required false) String keyword) { return Result.ok(medicineService.pageQuery(page, size, categoryId, keyword)); } }Service 层做条件判断MyBatis XML 里写动态 SQL。这里最容易翻车的地方是keyword为空字符串时数据库查不到结果所以我在 Service 做了StringUtils.isBlank(keyword) ? null : keyword.trim()。Mapper XML 里使用where标签让 SQL 只拼接非空条件select idpageQuery resultTypeMedicine SELECT * FROM medicine where if testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null AND name LIKE CONCAT(%, #{keyword}, %) /if AND status 1 /where ORDER BY create_time DESC LIMIT #{offset}, #{size} /select必须说清楚的一点这里 LIMIT 的offset和size来自 PageHelper 或者手动计算。PageHelper 一开始用起来会“玄学报错”比如明明分页了但数据还是整页返回我建议新手先用LIMIT #{offset}, #{size}这种手工分页不引入额外插件逻辑更直白。4.3 订单生成和库存扣减事务一致性药品信息展示做完了整个小程序最关键的是下单。我一般会把 createOrder 方法放在 OrderService 实现类里并用Transactional声明事务Service public class OrderServiceImpl implements OrderService { Autowired private OrderDao orderDao; Autowired private OrderDetailDao orderDetailDao; Autowired private MedicineDao medicineDao; Autowired private InvoiceDao invoiceDao; Transactional Override public void createOrder(OrderVO vo) { // 1. 校验商品是否存在且状态为上架 // 2. 扣减库存注意使用带条件的更新语句 int rows medicineDao.deductStock(vo.getMedicineId(), vo.getQuantity()); if (rows 0) { throw new RuntimeException(库存不足); } // 3. 插入主订单 Order order new Order(); order.setUserId(vo.getUserId()); order.setTotalAmount(vo.getTotalAmount()); orderDao.insert(order); // 4. 插入订单明细 // 5. 根据用户选择生成发票记录 invoiceDao.insert(...); } }库存扣减我用这样一条 SQLUPDATE medicine SET stock stock - #{quantity} WHERE id #{medicineId} AND stock #{quantity}这条 SQL 的语义是如果库存不够影响行数为 0Service 直接抛异常事务回滚不会出现超卖。连续两次点击下单如果库存只有 1 个第二个请求必定失败这也是在答辩时能被追问到并发问题时的安全答案。发票模块的做法比较直接用户选择开具发票后在后端生成一条 invoice 记录order_id 作为唯一字段防止同一笔订单生成两张发票。发票抬头、税号、金额都冗余存在 invoice 表里即使订单被删了发票历史也能查到。5. 避坑与排查Tomcat、MySQL 和小程序端对接的典型问题5.1 Maven 依赖下载慢或缺包先把镜像仓库换掉现象执行1-install.bat或mvn install时长时间卡在下载页面或者提示缺少 spring-webmvc、mybatis-spring 等依赖。原因Maven 默认走的中央仓库在国内访问很慢部分网络环境甚至会断流。解决在settings.xml的 mirrors 里配置阿里云镜像再重新更新依赖。mirror idaliyunmaven/id mirrorOfcentral/mirrorOf urlhttps://maven.aliyun.com/repository/public/url /mirror另外还要检查pom.xml里是否明确指定了 jar 包的 scope比如spring-webmvc是war包必须的依赖有的源码包注释掉了依赖导致 ClassNotFound这类问题把注释恢复即可。5.2 Tomcat 7 和 JDK 版本配对现象项目部署到 Tomcat 后启动失败报UnsupportedClassVersionError或者NoClassDefFoundError。原因Tomcat 7 本身基于 Java 7 编写但可以运行 JDK 8 编译出来的代码真正问题通常是你本地的 JDK 版本是 17编译时默认 class 文件版本过高。解决编译时在 pom.xml 里设置properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties并且 Tomcat 启动时选择 JDK 1.8 作为 JRE不要直接拿系统默认的高版本 JDK 去跑。微信开发者工具那侧不涉及 JDK但可能出现白屏一般是接口 404直接去查后端。5.3 MySQL 5.7 连接不上驱动版本和时区参数现象项目启动时打印Access denied for user或者Cannot create PoolableConnectionFactory。原因数据库密码不对只是表象更多情况是 mysql-connector-java 版本太高驱动默认加密方式与 5.7 不兼容。解决确认 JDBC 连接串的写法jdbc:mysql://localhost:3306/pharmacy?characterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai这里serverTimezone在高版本驱动里是必须项但 5.7 驱动写不写都不报错。如果用的是 Navicat 11 创建的库还要注意数据库本身是 utf8 还是 utf8mb4乱码大多是一侧 utf8、一侧 utf8mb4 导致的。5.4 小程序请求后端 404 或 405路由扫描问题现象后端明明是RequestMapping(/medicine/list)但小程序请求 404。原因有两个可能性一是 SpringMVC 配置文件里的context:component-scan没有扫到 controller 包二是前端 URL 拼接时带了多余前缀。解决检查 spring-mvc.xml 的 base-package并确认 Tomcat 下项目访问路径http://localhost:8080/pharmacy/medicine/list如果项目名是pharmacy前端请求的统一前缀就要写成/pharmacy/medicine/list而不是/medicine/list。404 还有一个隐藏原因是 Tomcat 根路径下没有部署这个项目war 包没被解压成功去tomcat/webapps看有没有对应的文件夹即可。5.5 登录状态不同步Token 还是 Session现象用户在小程序登录之后刷新页面状态就丢失或者订单接口拿到非本人数据。原因小程序原生开发没有 Cookie 自动带 Session必须手动把 Session 或自定义 Token 存在 storage 中请求时放进 header。解决后端在登录接口返回一个 token前端拿到后保存wx.setStorageSync(token, res.data.data.token);接下来的请求通过 header 传递wx.request({ header: { Authorization: wx.getStorageSync(token) }, url: http://localhost:8080/order/list, success() {} });我见过不少项目把用户 id 直接放在请求参数里这是安全黑洞谁都能伪造别人订单。答辩时至少说清楚 token 在前端存储、后端拦截器校验才能把用户管理模块立住。6. 验收与答辩从部署测试到把系统亮点讲清楚6.1 先按完整用例走一遍我拿到这种完整源码包不会急着改代码会先把后端启动、导入数据库、打开小程序三个动作全部跑通。验收清单一般是这样步骤预期结果实际检查管理员登录进入系统管理页面能看到用户列表菜单权限正确新增药品分类分类出现在小程序首页分类表插入记录新增药品小程序列表可看到该药品未上架状态不显示用户下单订单生成库存减少数据库 stock 变化申请发票发票记录生成且无法重复开票invoice 表仅一条记录这个流程走完至少证明主业务闭环是通的。验收时我会刻意做一个“非法操作”用普通用户账号去调商家接口如果返回无权限说明 Controller 权限校验起作用了而不是页面上把按钮藏起来这么简单。6.2 答辩时怎么把系统讲出亮点很多同学把项目跑起来之后只会讲页面和按钮。我建议从三个角度组织语言一是“为什么用 SSM”可以讲 Spring 管理事务、MyBatis 让 SQL 可维护然后再补一句“如果让我重做Spring Boot 会更快但 SSM 能更清楚看到底层配置”二是“数据库怎么防止超卖”直接把那两条更新库存的 SQL 写出来评委问并发的时候就答乐观锁思路三是“前端页面数据从哪来”强调是 JSON 接口而不是前端直接连数据库。这三点比背概念实在得多。从那以后我每次做项目的第一件事都是先跑通主流程、再写文档、最后再调样式不然样式改到一半接口出错整个下午都在黑匣子里翻车。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑