多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Spring Boot医药信息管理系统课设实战模板

Spring Boot医药信息管理系统课设实战模板 简介本资源是一套完整的数据库课程设计实践项目——医药信息管理系统面向高校计算机、信息管理及相关专业学生解决课程设计中缺乏真实业务场景与可运行参考系统的问题。系统覆盖药品/员工/客户/供应商等基本信息管理以及进货、库房、销售、财务四大核心业务模块具备入库登记、库存盘点、销售报表、月度统计等典型功能适合作为数据库原理、SQL开发与Java Web综合实训的落地案例。压缩包共335个文件含54个Java后端逻辑文件、44个JS前端交互脚本、38个HTML页面、12个CSS样式文件、9个XML配置及1个SQL建库脚本辅以Layui框架静态资源与Maven构建文件mvnw、pom.xml整体4.05MB结构清晰、开箱即用。已有72人学习下载提供可直接导入IDEA运行的完整工程含drug.iml、src目录及说明.txt涵盖数据库设计思路、模块划分逻辑与前后端协同要点是理解医药行业数据流与课设项目工程化落地的优质参考。1. 数据库课设医药信息管理系统一个能跑通、能改、能交的全栈实战模板这不是一个只有ER图和SQL脚本的“纸上谈兵”课设。我去年帮某高校三个班级调试过类似项目90%的学生卡在“本地跑不起来”——不是逻辑错是环境没配对、前端静态资源路径崩了、Maven依赖拉不全、MySQL表字段和Java实体类对不上。这个“数据库课设医药信息管理系统”本质是一个基于Spring Boot MyBatis Layui的可运行最小闭环系统它自带完整前后端代码、预置Layui UI组件不是截图是真实可用的layui.css/layer.css/laydate.css、甚至包含mvnw.cmd这种开箱即用的Maven Wrapper脚本。它解决的不是“要不要学数据库”而是“怎么把课本里的范式、约束、事务真正焊进一个带登录页、能查库存、能打销售报表的系统里”。适合大三下刚学完《数据库原理》、正被课设 deadline 追着跑的同学也适合想快速搭建教学Demo的助教——你不用从零搭Spring Boot不用手写Layui表格渲染逻辑更不用猜pom.xml里该加哪个MyBatis分页插件版本。它已经把“数据库设计→建表→后端CRUD→前端页面联动→报表导出”这条链路压进了一个能双击mvnw.cmd就启动的工程里。2. 从数据库建模到Spring Boot启动五步落地医药业务模型2.1 为什么选MySQL InnoDB不是因为“流行”而是课设场景下的刚性需求课设不是生产系统但必须体现数据库核心能力。这个项目默认使用MySQL原因很实在外键约束可验证药品表drug的supplier_id必须关联供应商表supplier学生能在Navicat里右键“查看外键”直观看到约束关系比口头讲“参照完整性”强十倍事务回滚可演示销售登记时要同时扣减库存、生成销售单、更新客户累计消费额——三步操作必须原子性。用Transactional包裹Service方法故意在第二步抛异常就能在控制台看到库存没扣、销售单没生成完美复现ACID中文索引无坑customer.name建了普通索引drug.description建了全文索引学生执行EXPLAIN SELECT * FROM drug WHERE MATCH(description) AGAINST(抗生素)时不会因字符集utf8mb4或排序规则utf8mb4_0900_as_cs报错。提示项目src/main/resources/application.yml中已预置MySQL连接配置但必须手动修改url中的IP和端口默认localhost:3306并确保你的MySQL服务正在运行。密码为空或root是常见初学者配置别直接复制粘贴就跑。2.2 基于业务模块反推数据库表结构一张图看懂5大功能如何映射到12张表系统五大功能模块基本信息、进货、库房、销售、财务不是平铺直叙而是通过主实体业务动作表耦合。例如“进货管理”不只有一张purchase_order采购单还必须有purchase_item采购明细来支持一单多药。我们按实际CREATE TABLE语句反向梳理核心表表名主键关键外键业务意义课设考察点drugid—药品主数据含category_id分类、unit_price单价范式分解分类信息应独立成表、非空约束name必填supplierid—供应商信息contact_phone带校验正则字段类型选择电话用VARCHAR而非INT、CHECK约束实践inventorydrug_idwarehouse_iddrug_id→drug.id库存快照表记录某药品在某仓库的实时数量联合主键设计、库存预警字段min_stock_levelpurchase_orderidsupplier_id→supplier.id采购单头含order_date、total_amount日期函数应用CURDATE()插入、金额精度DECIMAL(10,2)sales_recordidcustomer_id→customer.id销售单头含sales_date、payment_method枚举值实现payment_method用TINYINT存1现金/2微信/3医保注意inventory表没有自增ID而是用(drug_id, warehouse_id)作联合主键——这是课设里少有人深挖但极重要的设计避免同一药品在不同仓库重复建行强制业务逻辑在Service层校验“先查再插/更新”。2.3 Spring Boot后端MyBatis XML映射如何精准绑定Layui表格请求Layui的表格table.render()默认发送page当前页、limit每页条数、where搜索条件参数。后端不能靠RequestParam硬接必须用MyBatis动态SQL做适配。看DrugMapper.xml中库存查询片段select idselectInventoryWithCondition resultTypecom.example.drug.entity.InventoryVO SELECT i.id, d.name AS drug_name, d.specification, i.current_quantity, i.min_stock_level, w.name AS warehouse_name FROM inventory i LEFT JOIN drug d ON i.drug_id d.id LEFT JOIN warehouse w ON i.warehouse_id w.id where if testdrugName ! null and drugName ! AND d.name LIKE CONCAT(%, #{drugName}, %) /if if testwarehouseId ! null AND i.warehouse_id #{warehouseId} /if if testminQuantity ! null AND i.current_quantity lt; #{minQuantity} /if /where ORDER BY i.current_quantity ASC LIMIT #{offset}, #{limit} /select这段代码的关键不在语法而在参数传递逻辑#{offset}和#{limit}由Controller计算offset (page-1) * limit这是Layui分页的硬性要求#{drugName}来自Layui搜索框的where[drugName]不是request.getParameter(drugName)——MyBatis自动将Map的key转为参数名lt;是XML转义写成会报解析错误这是新手高频翻车点。血泪经验若Layui表格显示“暂无数据”先检查浏览器Network里请求是否带where[drugName]xxx再确认后端Controller是否用RequestBody MapString, Object params接收而非RequestParam最后看MyBatis日志是否打印了Binding: {drugName阿莫西林, offset0, limit10}——三者缺一不可。2.4 Layui前端CSS文件为何要列4次一次搞清静态资源加载链项目正文列出layui.css layer.css layer.css style.css style.css layui.mobile.css layui.mobile.css laydate.css看似冗余实则是Layui模块化加载的必然结果layui.css基础样式按钮、表单、导航栏layer.css弹出层样式layer.open()调用的对话框laydate.css日期选择器样式进货/销售单的日期控件style.css项目定制样式如药品列表页的“库存预警红色高亮”layui.mobile.css响应式适配在手机上打开仍可操作。避坑若页面按钮不显示图标、日期控件空白90%是CSS路径错误。检查thymeleaf模板中是否这样引入link relstylesheet th:href{/static/layui/css/layui.css} link relstylesheet th:href{/static/layui/css/laydate.css} link relstylesheet th:href{/static/css/style.css}注意/static/是Spring Boot默认静态资源目录layui/css/是解压后的实际路径不能写成/layui/css/少/static/或/css/layui.css路径错位。3. 启动失败页面空白Layui报错这5个坑我替你踩过了3.1 现象双击mvnw.cmd后控制台闪退或提示“找不到或无法加载主类”原因mvnw.cmd依赖JDK 8但Windows默认可能指向JDK 11而项目pom.xml中java.version指定为1.8版本不匹配导致类加载失败。解决命令行执行java -version确认当前JDK版本若高于1.8下载JDK 8u202课设兼容性最佳安装后设置系统环境变量JAVA_HOME指向JDK 8目录在项目根目录执行mvnw clean compile观察是否出现[INFO] BUILD SUCCESS。3.2 现象前端登录页能打开但点击“药品管理”跳转404Network显示GET /drug/list返回404原因Spring Boot默认静态资源路径为/static/**但Layui的JS路由如location.href/drug/list被当成静态资源拦截未进入Controller。解决检查WebMvcConfig.java中是否配置了addViewControllers().addViewController(/drug/list).setViewName(drug/list);更关键的是确认DrugController.java中RequestMapping(/drug)类注解存在且list()方法有GetMapping(/list)若用Thymeleaf确保src/main/resources/templates/drug/list.html文件存在且首行有!DOCTYPE htmlhtml xmlns:thhttp://www.thymeleaf.org。3.3 现象库存查询表格显示“加载中...”后空白浏览器Console报TypeError: laydate is not defined原因laydate.js未正确加载或加载顺序错误必须在layui.js之后。解决检查HTML中JS引入顺序script th:src{/static/layui/layui.js}/script script th:src{/static/layui/laydate.js}/script !-- 必须在layui.js之后 --确认laydate.js文件真实存在于src/main/resources/static/layui/laydate.js在laydate.render()调用前加判断if (typeof laydate ! undefined) { laydate.render({elem: #startTime, format: yyyy-MM-dd}); } else { console.error(laydate.js未加载); }3.4 现象新增药品时提交后提示“服务器内部错误”日志显示org.springframework.dao.DataIntegrityViolationException: Column name cannot be null原因前端表单中药品名称输入框input namename但后端Drug实体类的name字段未加NotBlank校验且数据库drug.name设为NOT NULLMyBatis插入时传入null触发约束异常。解决在Drug.java中添加校验NotBlank(message 药品名称不能为空) private String name;在Controller方法参数加ValidPostMapping(/save) ResponseBody public Result save(Valid RequestBody Drug drug) { ... }前端同步加JS校验if (!$(#name).val().trim()) { layer.msg(药品名称不能为空); return; }3.5 现象销售报表导出Excel打开后中文全是乱码如“药品”原因Apache POI生成Excel时未指定字符编码Windows系统默认用GBK而项目文本是UTF-8。解决在SalesReportService.java的导出方法中设置Workbook编码Workbook workbook new XSSFWorkbook(); Sheet sheet workbook.createSheet(销售报表); // 创建单元格时显式设置字体 Font font workbook.createFont(); font.setCharset(Font.DEFAULT_CHARSET); // 关键避免乱码 CellStyle style workbook.createCellStyle(); style.setFont(font);更彻底方案导出接口返回ResponseEntityResource设置Headerheaders.add(HttpHeaders.CONTENT_DISPOSITION, attachment; filename*UTF-8 URLEncoder.encode(销售报表.xlsx, UTF-8));4. 报表与统计用原生SQLMyBatis实现当日/当月财务统计绕过复杂BI工具4.1 当日统计一条SQL搞定销售额、成本、毛利无需定时任务课设不需要Kettle或Airflow当日统计的核心是利用MySQL的CURDATE()函数动态计算。看FinancialMapper.xml中selectTodayStatsselect idselectTodayStats resultTypecom.example.drug.entity.FinancialStats SELECT IFNULL(SUM(s.total_amount), 0) AS today_sales, IFNULL(SUM(pi.quantity * pi.unit_price), 0) AS today_cost, IFNULL(SUM(s.total_amount), 0) - IFNULL(SUM(pi.quantity * pi.unit_price), 0) AS today_profit FROM sales_record s LEFT JOIN sales_item si ON s.id si.sales_id LEFT JOIN purchase_item pi ON si.drug_id pi.drug_id AND pi.purchase_order_id IN ( SELECT id FROM purchase_order WHERE DATE(order_date) CURDATE() ) WHERE DATE(s.sales_date) CURDATE() /select这段SQL的精妙在于DATE(s.sales_date) CURDATE()精确匹配今日销售单避免sales_date 2024-06-01需手动改日期子查询pi.purchase_order_id IN (SELECT id FROM purchase_order WHERE DATE(order_date) CURDATE())关联今日采购单确保成本计算只取今日入库药品——这是课设里最容易被忽略的业务逻辑今日卖出的药其成本必须是今日采购的成本而非历史平均价IFNULL(..., 0)防止无数据时返回NULL导致Java端NullPointerException。提示若课设要求“当月统计”只需将CURDATE()替换为LAST_DAY(CURDATE())月末和DATE_SUB(CURDATE(), INTERVAL DAYOFMONTH(CURDATE())-1 DAY)月初组合成BETWEEN条件。4.2 销售TOP10药品用MyBatis动态SQL实现多维度筛选报表页常需“按月份查TOP10”或“按客户类型查TOP10”硬写10条SQL不现实。用MyBatischoose实现灵活切换select idselectTopDrugs resultTypecom.example.drug.entity.TopDrug SELECT d.name AS drug_name, SUM(si.quantity) AS total_quantity, SUM(si.quantity * si.unit_price) AS total_amount FROM sales_item si LEFT JOIN drug d ON si.drug_id d.id LEFT JOIN sales_record s ON si.sales_id s.id where choose when testtimeRange today AND DATE(s.sales_date) CURDATE() /when when testtimeRange thisMonth AND s.sales_date DATE_SUB(CURDATE(), INTERVAL DAYOFMONTH(CURDATE())-1 DAY) AND s.sales_date LAST_DAY(CURDATE()) /when otherwise AND s.sales_date #{startDate} AND s.sales_date #{endDate} /otherwise /choose if testcustomerId ! null AND s.customer_id #{customerId} /if /where GROUP BY d.id, d.name ORDER BY total_amount DESC LIMIT 10 /select此SQL支持三种时间范围timeRangetoday查今日TOP10timeRangethisMonth查本月TOP10自动计算月初月末其他值走otherwise分支用startDate/endDate手动指定。前端只需传{timeRange: thisMonth}后端零修改。4.3 报表导出用POI SXSSFWorkbook处理万级数据内存不爆课设演示若导出1万行销售记录用XSSFWorkbook会OOM。必须用流式APISXSSFWorkbookBean public Workbook createSalesReportWorkbook(ListSalesRecord records) { SXSSFWorkbook workbook new SXSSFWorkbook(100); // 每100行刷入磁盘 Sheet sheet workbook.createSheet(销售明细); // 写表头 Row headerRow sheet.createRow(0); String[] headers {单号, 客户, 药品, 数量, 单价, 金额, 时间}; for (int i 0; i headers.length; i) { Cell cell headerRow.createCell(i); cell.setCellValue(headers[i]); } // 写数据关键逐行flush不缓存全量 for (int i 0; i records.size(); i) { SalesRecord r records.get(i); Row row sheet.createRow(i 1); row.createCell(0).setCellValue(r.getOrderNo()); row.createCell(1).setCellValue(r.getCustomerName()); row.createCell(2).setCellValue(r.getDrugName()); row.createCell(3).setCellValue(r.getQuantity()); row.createCell(4).setCellValue(r.getUnitPrice()); row.createCell(5).setCellValue(r.getTotalAmount()); row.createCell(6).setCellValue(new SimpleDateFormat(yyyy-MM-dd HH:mm).format(r.getSalesDate())); // 每1000行主动flush释放内存 if (i % 1000 0 i 0) { ((SXSSFSheet) sheet).flushRows(1000); } } return workbook; }注意SXSSFWorkbook构造参数100表示内存中最多保留100行超出部分自动刷到临时文件。若课设数据量小1000行用XSSFWorkbook更简单若演示需要大数据量必须切SXSSFWorkbook否则Tomcat直接挂掉。5. 从课设到答辩三个让老师眼前一亮的实战技巧5.1 把“数据库设计说明书”变成可执行的SQL脚本答辩时现场建库课设文档常写“采用第三范式”但老师更想看你能否把范式理论落地为可运行的SQL。不要只交Word文档把database_design.sql做成可一键执行的脚本-- 创建数据库课设必须显式创建不能假定已有 CREATE DATABASE IF NOT EXISTS drug_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs; USE drug_management; -- 药品类别表体现范式避免药品表中重复存储“抗生素”“维生素” CREATE TABLE drug_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 类别名称如抗生素, description TEXT COMMENT 类别描述 ) ENGINEInnoDB COMMENT药品分类表; -- 药品主表外键关联类别符合3NF CREATE TABLE drug ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, category_id INT NOT NULL, unit_price DECIMAL(10,2) NOT NULL, specification VARCHAR(100), CONSTRAINT fk_drug_category FOREIGN KEY (category_id) REFERENCES drug_category(id) ) ENGINEInnoDB COMMENT药品主数据表;答辩时打开MySQL命令行粘贴执行当场展示SHOW CREATE TABLE drug;输出外键约束——比讲PPT强十倍。5.2 用Postman模拟真实业务场景证明系统不是“Hello World”老师最怕课设是“CRUD界面”要证明你理解业务闭环。用Postman发一组连贯请求第一步新增供应商POST http://localhost:8080/supplier/saveBody:{name:XX制药有限公司,contactPerson:张经理,phone:13800138000}第二步新增药品关联上一步供应商POST http://localhost:8080/drug/saveBody:{name:阿莫西林胶囊,categoryId:1,unitPrice:12.5,supplierId:1}第三步模拟入库触发库存增加POST http://localhost:8080/purchase/orderBody:{supplierId:1,items:[{drugId:1,quantity:100,unitPrice:8.2}]}第四步查询库存验证数量已更新GET http://localhost:8080/inventory/list?where[drugName]阿莫西林技巧把这四个请求保存为Postman Collection答辩时点“Run Collection”自动生成请求/响应时间轴老师一眼看到“业务流程真实可跑通”。5.3 在application.yml里埋一个“彩蛋配置”展示工程素养课设常被质疑“是不是抄的”一个细节就能破局在application.yml末尾加一段注释化的“扩展说明”# 课设扩展说明答辩时可展开 # 1. 安全加固已禁用H2 Consolespring.h2.console.enabledfalse避免生产误用 # 2. 日志分级WARN及以上输出到consoleDEBUG输出到logs/app.log便于问题追踪 # 3. 数据备份建议课设演示前执行 mysqldump -u root drug_management backup.sql # 4. 性能优化库存查询SQL已添加复合索引 idx_inventory_drug_warehouse (drug_id, warehouse_id) # 这段话本身不参与运行但告诉老师你不仅会写代码还知道课设之外的真实工程考量。从那以后我每次帮学生改课设都强制他们在application.yml里加这一段——不是为了应付而是逼自己思考“这个系统如果真上线还缺什么”。希望帮到你。本文还有配套的精品资源点击获取
返回列表