
简介这份资源是面向计算机专业毕业设计与课程设计场景的自助购药小程序完整源码包采用Java语言结合SSM框架与B/S架构开发后端以MySQL 5.7存储数据前端基于小程序技术实现适合需要完成电商类或医药零售类课题的学生与开发者参考学习。压缩包共1319个文件约22.55MB涵盖120个Java后端源码、138个Vue组件、181个JavaScript脚本、86个WXSS样式与84个WXML页面结构另含PNG、SVG等图片素材及SQL数据库文件前后端与数据库资源齐备。系统功能覆盖首页展示、个人中心、用户管理、商家管理、药品信息管理、药品分类管理、发票信息管理与系统管理完整呈现了在线购药的下单、支付与后台维护流程。目前已有57人学习读者可借此掌握SSM整合开发、小程序页面搭建与数据库设计的整体思路快速搭建可运行的项目原型为答辩与二次开发提供扎实基础。1. 从一份「基于小程序的自助购药小程序源代码java小程序mysqlLW」说起它到底能解决什么问题自助购药这个场景看起来只是把线下药店的货架搬到手机上但真正做过的人都知道难点从来不在「展示药品列表」而在处方药与非处方药的合规边界、库存与订单的一致性、以及用户下单后药师审核这一整套流程怎么在系统里跑通。一份标注了 java 小程序 mysql LW 的源代码本质上给的是「后端用 Java 写业务、前端用微信小程序做交互、数据落 MySQL、附带一份论文文档」的完整骨架。它适合三类人想拿它做课程设计或毕业设计的学生、想快速搭一个医药电商 Demo 验证商业模式的创业者、以及想研究小程序 Java 后端如何配合的初中级开发者。你拿到它之后最该关心的不是界面好不好看而是订单状态机、库存扣减时机、以及药品分类字段设计得合不合理——这三处决定了这份代码是能改造成真项目还是只能当个交作业的壳子。2. 拆开这份源代码Java 后端 小程序前端 MySQL 的职责怎么分2.1 三层结构里每一层到底管什么拿到一份陌生的购药小程序源码我一般先不看页面而是先找后端的 Controller 目录从接口反推业务。典型的 Java 后端多数是 Spring Boot会分三层Controller 接小程序的请求、Service 写业务逻辑比如下单时校验库存、生成订单号、扣减库存、Mapper 或 DAO 层跟 MySQL 打交道。小程序端则负责页面渲染和调用wx.request发请求它本身不该做任何业务判断——比如「库存够不够」这种判断必须放在后端前端只负责把结果展示出来。这里有个新手最容易搞混的点很多人以为小程序里写的if (stock 0)就算校验了其实那只是给用户看的提示真正的校验必须在 Service 层再做一遍否则有人直接构造请求就能绕过。所以读源码时你要重点确认「库存扣减」和「订单创建」是不是在同一个事务里这是判断这份代码能不能用的第一道门槛。2.2 数据库表设计药品、订单、用户三张核心表怎么读MySQL 这一层先看表结构再谈其他。一份能跑的自助购药系统至少要有药品表、订单表、订单明细表、用户表、购物车表。药品表里最关键的是分类字段和处方标识字段前者决定前端怎么筛选后者决定下单流程要不要走药师审核。下面是我读这类源码时习惯先跑的一段建表参考字段命名和类型都按医药场景的常见做法来-- 药品表分类和处方标识是医药场景的核心字段 CREATE TABLE medicine ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT 药品名称, category_id INT NOT NULL COMMENT 分类ID关联分类表, price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 单价用DECIMAL避免浮点误差, stock INT NOT NULL DEFAULT 0 COMMENT 库存, is_prescription TINYINT NOT NULL DEFAULT 0 COMMENT 0非处方 1处方药, status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 订单表状态字段是订单状态机的载体 CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 订单号唯一索引防重复, user_id INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待支付 1待审核 2已发货 3已完成 4已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明price和total_amount用DECIMAL而不是FLOAT是因为金额用浮点数会出现0.1 0.2 ! 0.3这种玄学问题对账时能让你查一整天。order_no加唯一索引是为了防止重复提交生成两笔订单。status字段的取值一定要在代码里定义成枚举别到处写魔法数字。参数说明is_prescription这个字段是医药场景区别于普通商城的关键处方药下单后不能直接进入发货流程必须先落到「待审核」状态由药师确认后才能继续。如果你的源码里没有这个字段那它大概率只是个普通商城改了个名字需要你自己补。2.3 小程序端怎么和后端对接请求封装与登录态小程序端读源码先找utils/request.js或类似的封装文件。好的封装会把 baseUrl、token 注入、错误统一处理都收在一处。登录态这块微信小程序的wx.login拿到 code 后要发给后端换 openid 和自定义 token后续请求都带这个 token。常见做法是后端用 JWT 或简单的 session 表来维护登录态。// utils/request.js 请求封装示例 const BASE_URL https://your-domain.com/api; function request(options) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { content-type: application/json, // 从本地缓存取 token登录后写入 Authorization: wx.getStorageSync(token) || }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { // token 失效跳登录 wx.navigateTo({ url: /pages/login/login }); reject(res.data); } else { wx.showToast({ title: res.data.msg, icon: none }); reject(res.data); } }, fail: reject }); }); } module.exports { request };逻辑说明把 token 注入和错误处理收在封装里页面里就只写request({ url: /medicine/list })改起来只改一处。401 单独处理是因为登录态过期是高频事件不统一处理的话每个页面都要写一遍跳转逻辑。参数说明BASE_URL要换成你自己的后端地址小程序正式环境必须用 https 且域名要在微信后台配置白名单本地调试可以在开发者工具里勾选「不校验合法域名」。Authorization这个 header 名字要和后端读取时保持一致前后端对不上是新手最常见的翻车点之一。3. 把这份源代码在本地跑起来环境、导入、联调的最小步骤3.1 后端环境准备与数据库导入先确认 JDK 版本多数这类源码是 JDK 8 或 11用java -version看一眼。然后装 MySQL导入源码里的.sql文件。导入前先建库字符集选utf8mb4否则药品名里的生僻字或特殊符号会变问号。# 1. 登录 MySQL 并建库 mysql -u root -p CREATE DATABASE pharmacy DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 2. 退出后用命令行导入 sql 文件注意路径换成你自己的 mysql -u root -p pharmacy /path/to/pharmacy.sql # 3. 验证表是否导入成功 mysql -u root -p -e USE pharmacy; SHOW TABLES;逻辑说明先建库再导入是因为很多 sql 文件里只有建表语句没有建库语句直接导入会报「No database selected」。用命令行导入比用图形工具稳大文件不容易中途断。参数说明utf8mb4而不是utf8是因为 MySQL 的utf8实际只支持 3 字节存不了 emoji 和部分生僻字药品说明书里这类字符不少。导入后如果表是空的检查 sql 文件里有没有INSERT语句很多课程设计源码只给结构不给数据。3.2 改配置文件里的数据库连接和端口后端跑不起来八成是配置文件没改。找到application.yml或application.properties把数据库地址、用户名、密码改成你本地的。同时确认端口没被占用。# application.yml 关键配置 server: port: 8080 # 后端端口被占用就换 8081 spring: datasource: url: jdbc:mysql://localhost:3306/pharmacy?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password # 改成你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver逻辑说明serverTimezoneAsia/Shanghai这个参数不加MySQL 8 以上版本经常报时区错误订单时间会差 8 小时这是血泪经验。driver-class-name在 MySQL 8 里是com.mysql.cj.jdbc.Driver老版本是com.mysql.jdbc.Driver写错会报驱动找不到。参数说明url里的pharmacy要和你实际建的库名一致。密码别用特殊字符否则在 yml 里可能要转义新手直接用简单密码本地调试即可。3.3 小程序端导入与真机预览用微信开发者工具打开小程序目录填上你的 AppID没有就用测试号。然后在request.js里把BASE_URL改成后端地址。本地调试时后端跑在localhost:8080但小程序开发者工具里localhost指向的是工具本身需要在「详情 - 本地设置」里勾选「不校验合法域名」并且用http://localhost:8080访问。真机预览时localhost就不通了因为手机和电脑不是同一台设备。常见做法是把后端部署到有公网地址的服务器或者用内网穿透工具把本地端口映射出去。这一步是新手卡得最久的地方不是代码问题是网络可达性问题。4. 自助购药业务里最容易翻车的几个地方避坑与排查4.1 库存扣减时机不对导致超卖现象两个人同时下单同一款药库存只有 1 件结果两笔订单都创建成功库存变成 -1。原因先查库存再扣库存两步之间没有加锁并发时都读到了「库存 1」都判断通过。解决把扣减写成带条件的原子更新UPDATE medicine SET stock stock - 1 WHERE id ? AND stock 1然后看影响行数为 0 就说明库存不足回滚订单。这样数据库层面就保证了不会超卖。4.2 订单状态用魔法数字改一处漏一处现象订单状态判断散落在各个 Service 和前端页面里加一个新状态时改漏了某处出现订单卡在中间状态。原因到处写if (status 1)没有统一定义。解决定义枚举类OrderStatusEnum所有判断都引用枚举。前端也用常量对象映射状态文案别在 wxml 里写死数字。4.3 处方药没走审核流程直接发货现象处方药下单后直接进入待发货没有药师审核环节。原因源码本身是普通商城改的is_prescription字段没接进订单流程。解决在创建订单的 Service 里判断如果订单里含处方药状态置为「待审核」而不是「待支付」或「待发货」并生成一条审核记录。审核通过后再流转到发货。4.4 金额计算用 double 导致对账差几分钱现象购物车多件商品总价和明细相加对不上差 0.01。原因Java 里用double算金额浮点精度丢失。解决金额统一用BigDecimal数据库用DECIMAL。BigDecimal做加法用.add()比较用.compareTo()而不是.equals()因为equals会比较精度1.0和1.00不相等。4.5 小程序请求域名没配白名单真机一片空白现象开发者工具里正常真机上所有接口都失败。原因微信要求正式环境请求的域名必须在后台配置且必须是 https。解决开发阶段在开发者工具勾选「不校验合法域名」真机调试时要么配好 https 域名要么用内网穿透拿到一个 https 地址。别想着绕过这是平台硬性要求。5. 从能跑到能用把这份源码改造成可演示项目的三个技巧5.1 用状态机把订单流程收拢成一张图订单状态是这类项目最容易乱的地方。我一般会先画一张状态流转表把「什么操作触发什么状态变更」写清楚再照着写代码。下面这张表可以直接抄当前状态触发操作目标状态校验条件待支付用户支付成功待审核/待发货含处方药则待审核待审核药师通过待发货审核记录存在待审核药师驳回已取消需填驳回原因待发货商家发货已发货有物流单号已发货用户确认已完成无待支付超时未付已取消创建超 30 分钟有了这张表Service 里就只写「当前状态 操作 → 目标状态」的判断任何不在表里的流转直接拒绝。这样加需求时改表就行代码逻辑不用大动。5.2 给关键接口加一层参数校验小程序端传上来的参数永远不能信。用Valid加注解做基础校验比如药品 ID 不能为空、数量必须大于 0。更关键的是业务校验比如下单时数量不能超过库存、不能超过单次限购。这些校验放在 Service 入口别散落在各个方法里。// 下单参数校验示例 public Order createOrder(CreateOrderDTO dto) { if (dto.getItems() null || dto.getItems().isEmpty()) { throw new BizException(购物车为空); } for (OrderItemDTO item : dto.getItems()) { if (item.getQuantity() 0) { throw new BizException(购买数量必须大于0); } Medicine med medicineMapper.selectById(item.getMedicineId()); if (med null || med.getStatus() 0) { throw new BizException(药品不存在或已下架); } if (med.getStock() item.getQuantity()) { throw new BizException(库存不足 med.getName()); } } // 校验通过后再走扣减和创建订单 return doCreateOrder(dto); }逻辑说明先做完整校验再执行写操作避免扣了一半库存才发现某个药品不够。BizException是自定义业务异常统一被全局异常处理器捕获后返回给前端友好提示。参数说明CreateOrderDTO里的字段用包装类型Integer而不是int因为int默认值是 0前端没传时你分不清是「没传」还是「传了 0」包装类型默认 null 就能区分。5.3 用日志把「黑匣子」打开项目跑起来后出问题没有日志就是抓瞎。至少在订单创建、库存扣减、支付回调这三个地方打日志记录订单号、用户 ID、关键参数和结果。日志级别用info记正常流程error记异常。别用System.out.println用 SLF4J。private static final Logger log LoggerFactory.getLogger(OrderService.class); public Order doCreateOrder(CreateOrderDTO dto) { log.info(开始创建订单, userId{}, itemCount{}, dto.getUserId(), dto.getItems().size()); try { // ... 业务逻辑 log.info(订单创建成功, orderNo{}, amount{}, order.getOrderNo(), order.getTotalAmount()); return order; } catch (Exception e) { log.error(订单创建失败, userId{}, dto.getUserId(), e); throw e; } }逻辑说明日志里带上订单号和用户 ID出问题时能直接 grep 定位。异常日志一定要把e传进去否则只有一行「订单创建失败」堆栈信息全丢了排查时等于没有后悔药。参数说明日志占位符用{}而不是字符串拼接因为拼接无论日志级别开不开都会执行占位符只在需要输出时才替换性能更好。最后说个我自己的习惯拿到任何一份源码先别急着改界面先花半天把「下单 → 扣库存 → 生成订单 → 状态流转」这条主链路在本地跑通跑不通就先修主链路界面丑不丑是最后的事。主链路通了这份代码才有改造价值主链路不通界面再漂亮也只是个壳。希望帮到你。本文还有配套的精品资源点击获取