ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于SpringBoot的药店供销系统设计:从架构到库存管理的完整实践

基于SpringBoot的药店供销系统设计:从架构到库存管理的完整实践 1. 从毕设题目到完整系统这个药店供销系统到底要做什么作为计算机专业的毕业生拿到“基于SpringBoot的森伯药店供销系统”这个题目时第一反应可能是这又是一个典型的CRUD管理系统。但实际动手后你会发现医药零售进销存远比想象中复杂它不只是简单的增删改查而是涉及采购、销售、库存、效期管理、财务对账、供应链协同等多个业务环节的综合性系统。这也是为什么这类题目在毕业设计中长盛不衰的原因它既有技术深度又有业务复杂度拿出来讲述时有实实在在的内容可以讲。先说清楚这个系统解决的核心问题。药店零售和普通商品零售最大的区别在于三个字:管效期。药品有严格的有效期管理过期药品不允许销售这就导致库存管理不能只看数量还要看批次、看生产日期、看距过期时间。同时药品属于特殊商品进货必须有供应商资质、批号记录销售必须有处方登记特别是处方药这就让供销系统不能只做简单的出入库流水还需要建立供应商档案、药品档案、批号追踪机制。再看这个题目的另一个关键词:智慧药房供应链协同。拆开来讲智慧体现在数据分析上——哪些药卖得快、哪些药滞销、哪些药即将过期需要促销处理这些需要系统自动给出提示供应链协同体现在采购和销售的信息打通上——采购单下达后能追踪到货状态销售数据能反哺采购建议库存不足时能自动生成补货提醒。把这些需求落到SpringBoot项目里就是一套完整的业务闭环。技术选型上题目明确指出SpringBoot这很合理。SpringBoot本身就是Java生态里最适合快速搭建业务系统的框架它的自动配置特性让开发者不用关注大量XML配置内置Tomcat让部署变得简单Restful风格的接口设计和Spring家族天然契合。加上MyBatis Plus做持久层、MySQL做存储、Vue做前端这套组合在中小企业实际项目和毕业设计中都是标配。很多同学纠结要不要上微服务、要不要用Redis、要不要用MQ我的建议是:单体架构 必要时用Redis做缓存就够了。毕业设计的核心是完整跑通业务、讲清楚设计思路过度设计反而会给自己挖坑。2. 整体设计与技术选型为什么我用这套方案2.1 单体架构优先别急着上微服务先说架构选型这件事。森伯药店供销系统的业务量级即便是真实药店单店每天的订单量也就是几百单数据量完全在单库单表的处理能力范围内。这个前提下用微服务架构就是给自己找麻烦——服务拆分、分布式事务、服务注册发现、配置中心每一个环节都会消耗大量精力和时间而且这些内容一旦写进论文答辩时老师追问起来自己很容易答不深。我采用的方案是标准的前后端分离单体架构。SpringBoot负责提供RESTful APIVue负责页面展示和交互MySQL存储业务数据MyBatis Plus做数据持久化。考虑到医药系统的特殊性我用Spring Security JWT做登录认证和权限控制用拦截器统一处理Token校验。这套架构的好处是简单清晰每一层的职责边界分明代码写起来、论文写起来都顺。2.2 核心业务模块划分系统我分成五个核心模块这也是进销存系统的通用划分方式:基础资料模块:药品信息、供应商信息、客户信息、仓库信息。药品信息里要包含通用名、商品名、规格、剂型、生产厂家、批准文号、零售价、会员价、采购价、库存上下限。为什么字段这么多因为药店的运营需要精细化不同厂家同一成分的药价格差异很大医生开处方一般写商品名顾客找药也习惯认厂牌字段不完整后面做统计、做采购建议就会缺东西。采购管理模块:采购订单、采购入库、退货。采购订单的生成不能靠业务员随便填要能参考库存预警数据低于安全库存的药品自动进入建议采购清单。销售管理模块:前台收银、销售退货、处方登记。销售是整个系统的核心一笔销售至少涉及商品表、库存表、销售明细表、批次表四张表的数据变化还得处理处方药登记这种特殊需求。库存管理模块:库存查询、库存盘点、效期预警、库存调拨。这个模块直接体现医药系统的特殊性做普通进销存可能只需要管数量这里必须管批次和效期。统计分析模块:销售统计、毛利分析、供应商排名、采购分析。毕业设计之所以加分就是在展示的时候能从统计报表里讲出业务洞察。2.3 数据库设计的关键细节数据库设计是这类系统的重头戏。我见过很多同学建表时只考虑当前功能不考虑业务扩展结果做到后面各种改表。进销存系统的表结构有几张表的设计思路必须提前想清楚。第一药品表。不要在药品表里直接放库存数量。很多人犯这个错误药品表设计一个stock字段然后每次出入库就update这个字段。表面看起来简单实际上一旦发生退货、盘点差异、批次调拨这个字段就会变得不可信。正确做法是建立独立的库存表stock按药品仓库批次维度维护当前库存数量通过库存流水表stock_record记录每一次库存变动。药品表只存基础信息库存和商品信息彻底解耦。第二批号管理。药品进出必须记录批次号和生产日期。为什么因为药品效期监管是刚需不同批次的生产日期不同有效期也不同。系统需要能查出“某批药品是什么时候进的、目前剩余多少、什么时候过期”。所以入库单明细里除了药品ID还必须有batch_no、production_date、expire_date三个字段库存表要按药品批次去重。第三订单状态机。采购单和销售单不能只用一个“状态”字段存字符串因为流程中存在多个状态流转节点。采购单我设计了草稿、待审核、已审核、已入库、已完成、已作废六个状态销售单设计了待支付、已支付、已退货、已作废四个状态。用数字字典存状态值代码里写枚举转换保证状态流转可控。下面是我总结的几张核心表的结构参考:药品表 (drug) - id, drug_code, generic_name, trade_name, specification, dosage_form - manufacturer, approval_number, unit, purchase_price, retail_price - member_price, stock_upper_limit, stock_lower_limit, status, create_time 批次库存表 (stock) - id, drug_id, batch_no, production_date, expire_date, quantity, warehouse_id 库存流水表 (stock_record) - id, drug_id, batch_no, change_type, quantity_before, quantity_after - change_quantity, relation_no, create_time 采购单主表 (purchase_order) - id, order_no, supplier_id, total_amount, status, audit_user, audit_time - create_user, create_time 采购单明细表 (purchase_order_item) - id, order_id, drug_id, batch_no, quantity, price, amount 销售单主表 (sale_order) - id, order_no, member_id, total_amount, discount_amount, pay_type - status, sale_user, create_time 销售单明细表 (sale_order_item) - id, order_id, drug_id, batch_no, quantity, price, amount3. 核心业务流程与关键代码实现3.1 采购入库流程:库存和批号如何同步更新采购入库是整个进销存链条的起点也是批次管理的入口。常规逻辑是:采购员建单 → 审核员审核 → 库管员入库。入库操作发生时系统要同时做两件事:更新采购单状态、生成库存流水并更新批次库存表。很多人实现入库时只做了“采购单状态改为已入库”和“库存数量增加”忽略了批号信息导致后面做效期预警时根本没有数据可用。正确的入库处理核心代码逻辑应该是这样的:Override Transactional(rollbackFor Exception.class) public void stockIn(PurchaseStockInDTO dto) { // 1. 校验采购单状态必须是待入库状态 PurchaseOrder order purchaseOrderMapper.selectById(dto.getOrderId()); if (order null || !PurchaseOrderStatus.WAIT_IN.equals(order.getStatus())) { throw new BizException(采购单状态不合法无法入库); } // 2. 查询采购单明细逐条处理库存 ListPurchaseOrderItem items purchaseOrderItemMapper.selectList( new LambdaQueryWrapperPurchaseOrderItem() .eq(PurchaseOrderItem::getOrderId, dto.getOrderId()) ); for (PurchaseOrderItem item : items) { // 2.1 生成或更新批次库存 Stock stock stockMapper.selectByDrugAndBatch(item.getDrugId(), dto.getBatchNo()); if (stock null) { // 批次不存在则创建新批次 stock new Stock(); stock.setDrugId(item.getDrugId()); stock.setBatchNo(dto.getBatchNo()); stock.setProductionDate(dto.getProductionDate()); stock.setExpireDate(dto.getExpireDate()); stock.setQuantity(item.getQuantity()); stock.setWarehouseId(dto.getWarehouseId()); stockMapper.insert(stock); } else { stock.setQuantity(stock.getQuantity() item.getQuantity()); stockMapper.updateById(stock); } // 2.2 记录库存流水 StockRecord record new StockRecord(); record.setDrugId(item.getDrugId()); record.setBatchNo(dto.getBatchNo()); record.setChangeType(StockChangeType.PURCHASE_IN.getCode()); record.setChangeQuantity(item.getQuantity()); record.setRelationNo(order.getOrderNo()); stockRecordMapper.insert(record); // 2.3 更新采购明细的批次信息 item.setBatchNo(dto.getBatchNo()); purchaseOrderItemMapper.updateById(item); } // 3. 更新采购单状态 order.setStatus(PurchaseOrderStatus.STOCKED_IN.getCode()); order.setStockInTime(new Date()); purchaseOrderMapper.updateById(order); }这段代码有两点值得说明。第一用了Transactional事务注解因为一次入库涉及多张表的update/insert任何一个环节失败都可能导致库存和单据数据不一致事务保证要么全部成功要么全部回滚。第二批次库存的处理用了“先查后插”的方式如果该药品该批次已经存在比如分批到货就在原批次上累加数量如果不存在就新建记录。这是批次管理的核心逻辑后面做效期预警和批号追踪都依赖这里的数据完整性。3.2 销售出库:先进先出原则怎么实现销售出库比采购入库逻辑上更复杂一些核心难点在先进先出FIFO。为什么要先进先出因为药店法规要求近效期药品先销售防止药品过期。如果系统不控制出库批次库管员凭感觉发货就会导致先进来的药品一直压着后面的药都卖完了前面的药过期报废。先进先出的实现逻辑是这样的:Override Transactional(rollbackFor Exception.class) public void saleOut(SaleOrderDTO dto) { // 1. 创建销售单信息 SaleOrder order createSaleOrder(dto); // 2. 对销售单中的每个药品按批次效期排序扣减库存 for (SaleOrderItemDTO itemDTO : dto.getItems()) { int needQuantity itemDTO.getQuantity(); // 查询该药品所有大于0的批次按有效期限升序排列 ListStock stocks stockMapper.selectValidStocksByExpire( itemDTO.getDrugId(), dto.getWarehouseId()); for (Stock stock : stocks) { if (needQuantity 0) break; int deductQuantity Math.min(needQuantity, stock.getQuantity()); // 扣减当前批次的库存 stock.setQuantity(stock.getQuantity() - deductQuantity); stockMapper.updateById(stock); // 记录销售流水的批次信息 SaleOrderItem saleItem new SaleOrderItem(); saleItem.setOrderId(order.getId()); saleItem.setDrugId(itemDTO.getDrugId()); saleItem.setBatchNo(stock.getBatchNo()); saleItem.setQuantity(deductQuantity); saleOrderItemMapper.insert(saleItem); // 写库存流水 saveStockRecord(itemDTO.getDrugId(), stock.getBatchNo(), StockChangeType.SALE_OUT, deductQuantity, order.getOrderNo()); needQuantity - deductQuantity; } if (needQuantity 0) { throw new BizException(药品[ itemDTO.getDrugId() ]库存不足); } } }关键在selectValidStocksByExpire这个方法SQL里按expire_date升序排列优先扣减最早过期的批次。实际项目中你还可以加一个条件:排除已经过期的批次。查询时加上expire_date NOW()就行防止把过期药卖出去——这是医药系统不可触碰的红线。3.3 效期预警:定时任务与查询逻辑效期预警是药店系统里非常加分的功能。它的业务规则是:药品距离有效期截止日不足90天时提示“近期失效”不足30天时提示“紧急处理”。毕业设计中我建议用Spring Boot自带的Scheduled定时任务每天凌晨跑一次效期检查把预警数据写入一张预警表同时前端在首页展示预警信息栏。Component public class ExpireDateCheckTask { Scheduled(cron 0 0 2 * * ?) // 每天凌晨2点执行 public void checkExpiringDrugs() { // 查询90天内到期的批次库存 ListStock expiringStocks stockMapper.selectExpiringStocks(90); for (Stock stock : expiringStocks) { Drug drug drugMapper.selectById(stock.getDrugId()); int daysToExpire calculateDaysToExpire(stock.getExpireDate()); // 生成预警记录 ExpireWarning warning new ExpireWarning(); warning.setDrugId(drug.getId()); warning.setDrugName(drug.getGenericName()); warning.setBatchNo(stock.getBatchNo()); warning.setQuantity(stock.getQuantity()); warning.setExpireDate(stock.getExpireDate()); warning.setDaysToExpire(daysToExpire); warning.setStatus(0); // 未处理 expireWarningMapper.insert(warning); } } }定时任务可以用自定义的开关配置既可以直接在配置文件里用Value绑定一个计划任务开关参数也可以做成配置项存数据库在管理后台手动控制便于答辩现场演示——你可以在界面上改一个参数然后触发任务执行比干巴巴讲原理更有说服力。3.4 前端页面与技术整合前端我选的是Vue Element UI这是SpringBoot后台管理系统最经典的搭档。Vue负责数据双向绑定和组化开发Element UI提供现成的表格、表单、对话框组件做出来的界面比较符合主流管理系统的观感。页面与后端通过Axios调用API登录后把JWT Token存在LocalStorageAxios请求拦截器统一加Token响应拦截器统一处理401跳转登录。说一个实际经验:不要在前端写太多业务逻辑。很多同学喜欢在前端判断库存是否充足、计算订单金额这是不对的做法。库存的判断必须在后端做因为前端逻辑很容易被绕过而且一旦多个用户同时操作前端计算的值是不可靠的。前端只做展示和基本的表单校验真正的业务规则全部放在后端Service层。 前端页面设计方面我的建议是重点打磨三个页面:登录页、收银台页面、库存预警首页。答辩时老师通常不会每个页面都点一遍但他一定会关注这几个体现系统核心价值的页面。收银台页面的商品搜索要支持扫码枪输入直接输入条码回车查询支持拼音首字母搜索支持会员信息带入折扣;库存预警首页要用醒目的颜色标识不同紧急程度的预警药品这能直观展示系统的“智慧”属性。 ## 4. 报表设计与锐浪服务器整合:让数据会说话 ### 4.1 为什么选择锐浪报表服务器 很多毕设系统的统计分析功能直接用ECharts画图做销售趋势、分类占比等图表。图表确实直观但有一个问题:传统的报表场景需要打印或导出成PDF比如药品盘点表、销售日报、采购入库单。这些带有格式要求的单据用ECharts很难实现像素级的打印排版。所以我在系统里引入了锐浪报表服务器GridReport专门负责业务单据和统计报表的生成与打印。 锐浪报表和SpringBoot整合的总体思路是:后端报表引擎负责根据模板填充数据生成报表文件;前端通过报表控件展示和打印。实际项目中锐浪报表除了支持Java版本很多时候是在ASP.NET或桌面端用的多但Java环境下集成也完全可行——核心是通过HTTP接口调用报表服务传入参数和JSON数据返回渲染好的报表结果给前端。 ### 4.2 集成实战:报表模板与数据填充 具体的整合步骤我整理了一下: **第一步设计报表模板。** 在锐浪报表设计器中先绘制出采购入库单、销售日报、库存盘点表等模板设置好表格区域、动态数据字段比如循环列表绑定数据集保存为模板文件。 **第二步后端提供数据接口。** 报表展示的数据来源还是我们的SpringBoot服务。比如销售日报报表需要查询指定日期范围内的销售汇总数据: java GetMapping(/report/saleDaily) public ResultSaleDailyReportVO saleDailyReport( RequestParam String startDate, RequestParam String endDate) { // 按日期分组统计销售额、订单数、毛利等指标 ListSaleDailyVO list saleOrderMapper.selectDailyReport(startDate, endDate); // 组装成报表模板要求的数据结构 SaleDailyReportVO vo new SaleDailyReportVO(); vo.setDays(list); return Result.success(vo); }第三步前端加载报表。前端页面通过锐浪报表的JavaScript控件把后端返回的JSON数据喂给模板进行渲染展示。这个环节要注意编码问题数据里包含中文时报表控件默认的编码格式容易产生乱码建议统一使用UTF-8编码并且在传参时对特殊字符做转义处理。这里给大家一个踩坑提示:锐浪报表的Java集成和打印预览在国产化浏览器或部分老旧浏览器上可能会遇到插件不兼容的情况。毕业设计演示时建议提前用Chrome稳定版测试好并且准备一套导出为PDF的方案作为备选——从后端用JasperReports或POI生成PDF这样即使报表插件出问题答辩现场也能通过PDF展示成果不至于冷场。4.3 统计报表的SQL实现技巧报表模块的核心在SQL。进销存系统里最常用也最容易出错的统计是“进销存汇总表”这种报表适合做进销存分析的通用模板。我给你一个可以放心套用的SQL思路:-- 按药品统计某时间段的采购、销售、退货汇总 SELECT d.drug_code, d.generic_name, d.specification, -- 采购数量 COALESCE(SUM(CASE WHEN sr.change_type 1 THEN sr.change_quantity ELSE 0 END), 0) AS purchase_qty, -- 销售数量 COALESCE(SUM(CASE WHEN sr.change_type 2 THEN sr.change_quantity ELSE 0 END), 0) AS sale_qty, -- 退货数量 COALESCE(SUM(CASE WHEN sr.change_type 3 THEN sr.change_quantity ELSE 0 END), 0) AS return_qty, -- 当前库存取药品所有批次最新数量之和 (SELECT COALESCE(SUM(s.quantity), 0) FROM stock s WHERE s.drug_id d.id) AS current_stock FROM drug d LEFT JOIN stock_record sr ON sr.drug_id d.id AND sr.create_time BETWEEN #{startTime} AND #{endTime} GROUP BY d.id写这个报表SQL有几个坑要注意。第一COALESCE是必须的否则没有流水的药品SUM结果是NULL前端显示就会出现空白或JSON序列化问题。第二这里使用LEFT JOIN是为了保证没有业务记录的药品也能出现在报表里显示采购和销售都为0这在盘点时非常有用。第三当前库存如果查询量太大要在药品表加冗余字段来提高性能否则为了算一个库存字段去无限联表统计数据量大了会卡死。5. 系统优化:性能、安全与经验总结5.1 分页查询必须用MyBatis的分页插件进销存系统里最频繁的操作就是列表查询药品列表、采购单列表、销售单列表全是分页查询。MyBatis Plus自带的分页插件PaginationInnerInterceptor是标配三行代码就能配置好:Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }分页插件原理是拦截器在执行SQL前自动拼接LIMIT语句所以你在Service层只要写普通的查询方法传一个Page对象进去就能返回分页结果:// 分页查询药品列表 PageDrugVO page new Page(current, size); LambdaQueryWrapperDrug wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(keyword), Drug::getGenericName, keyword) .orderByDesc(Drug::getCreateTime); PageDrugVO result drugMapper.selectPage(page, wrapper);坑点提醒:药品名称模糊查询时如果用户输入了%或_默认情况下MyBatis会把它当成通配符处理导致查出所有数据。这是Like查询的注入风险要么前端限制输入要么后端对关键字做转义:keyword.replace(%, \\%).replace(_, \\_)。5.2 文件上传的安全过滤系统里如果有PDF处方上传功能一些特殊药品要求上传电子处方注意全局过滤器对文件内容的处理。网上有案例提到SpringBoot全局过滤器处理上传文件时的XSS攻击问题这里我提醒一下:过滤器的getParameter方法只能拿表单参数文件上传用的是getInputStream如果你的过滤器对请求体做了XSS清理可能会把文件二进制数据也给截断掉导致上传的PDF打不开。正确的做法是:在过滤器中判断Content-Type如果是multipart/form-data类型直接放行不做XSS清理XSS风险主要存在于表单文本字段和URL参数文件本身要另外做病毒扫描和扩展名校验。5.3 事务与并发:库存扣减不超卖药店收银台可能存在多人同时操作如果不用数据库锁控制库存扣减在并发场景下会超卖。具体场景:某种药库存只剩1盒两个收银员同时收到订单都查出库存为1各自扣成0最后库存变成-1订单却卖出去了2盒。避免超卖有几种做法:方案一乐观锁。在库存表加一个version字段更新时带上version判断:UPDATE stock SET quantity quantity - #{deductQuantity}, version version 1 WHERE id #{stockId} AND version #{oldVersion} AND quantity #{deductQuantity}影响行数为0时说明数据已被修改或库存不足抛异常回滚。这种方案在毕业设计项目中完全够用。方案二悲观锁。用SELECT ... FOR UPDATE锁定行记录等事务提交后再释放锁。并发量小的时候更可靠但会有锁等待问题实现起来也比乐观锁复杂。我在毕业设计里用的是乐观锁因为实现简单好讲解老师也认可这个设计思路。5.4 SpringBoot版本选择的经验关于版本网上讨论很多我自己也踩过坑。毕业设计项目我建议用SpringBoot 2.7.x理由很实际:这是2.x系列的收官版本稳定、文档多、网上遇到的问题基本都有解决方案。SpringBoot 3.x要求JDK 17及以上很多学校的教学环境还停留在JDK 8加上部分旧版依赖和3.x存在兼容问题没必要冒险。我见过有人纠结“版本太低会不会被扣分”实际上答辩老师关注的是你对技术选型的理解而不是版本号的新旧。你只要能说清楚“为什么选2.7.x、为什么不用3.x”反而是加分项。另外Docker部署SpringBoot项目也是一个可以讲的亮点项目里准备好Dockerfile和docker-compose.yml答辩时演示容器化部署导师会认为你有工程化意识。Dockerfile可以这样写:FROM openjdk:8-jre-alpine WORKDIR /app COPY target/pharmacy-system.jar /app/app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]** 尖括号这里出错了应该是:FROM maven:3.8-jdk-8 AS build WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:8-jre-alpine WORKDIR /app COPY --frombuild /build/target/pharmacy-system.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]这种多阶段构建的方式可以保证镜像体积小部署时快同时把Maven构建过程和运行时隔离更专业。6. 常见问题速查与排查实录做这类毕设下面几个问题是我以及身边同学反复遇见的提前排掉这些坑你能省出很多睡眠时间。问题现象根本原因解决方案启动报Failed to configure a DataSource没有配置数据源或配置了多数据源但主数据源未指定检查application.yml中spring.datasource配置;多数据源时用Primary标记主数据源分页查询总数错误或查询结果不对MyBatis Plus分页插件未生效确认MyBatisPlusConfig类加了Configuration注解且配置类的扫描路径正确日期字段返回给前端少8小时或格式不对时区问题或未配置JSON日期格式JDBC连接串加serverTimezoneAsia/ShanghaiJackson配置spring.jackson.date-formatyyyy-MM-dd HH:mm:ss库存扣减出现负数并发超卖或扣减逻辑没有校验库存使用乐观锁库存充足性校验参考5.3的方案报表打印中文乱码数据库连接编码或报表控件编码不匹配统一使用utf8mb4编码连接串加characterEncodingutf8报表数据传递时做URL编码JWT登录一段时间后失效Token过期时间设置过短根据业务设置合理的过期时间如2小时刷新Token机制保证良好体验前端跨域请求失败前后端分离时未配置跨域后端加CORS配置或使用网关代理开发环境用Vue proxy代理是最省事的方式再补充一个比较隐蔽的问题:IDEA创建SpringBoot项目时如果你本机的JDK是11或17而你在网上找的学习资料是JDK8的写法容易出现编译错误。这里我的建议是:项目语言级别和SDK版本要严格统一比如全用JDK8IDEA的Project Structure里Project SDK和Project language level都改成8。如果IDEA始终不让你选JDK8可以检查系统环境变量JAVA_HOME是否指向了正确版本或者直接用Spring Initializr网页版生成包后再导入IDEA不要用IDEA内置的创建向导。7. 结语:从一个想法到一套可讲课的系统做了这么多这套系统的价值其实已经不只是“能跑”了。从技术层面它覆盖了SpringBoot、MyBatis Plus、Spring Security、JWT、Vue、Element UI、Docker这些毕业设计的主流技术栈;从业务层面它真实反映了医药零售行业的进销存痛点——效期管理、批次追踪、供应链联动。答辩的时候你可以从数据库设计聊到事务一致性从前端交互聊到报表打印从单机部署聊到容器化交付每一层都有扎实的细节可以讲。我个人在实际操作中的体会是:完成这类系统最重要的不是炫技而是把技术选型和业务需求之间的逻辑关系想透。比如为什么用乐观锁而不是悲观锁为什么库存要独立成表而不是冗余在药品表里为什么销售要按批次先进先出——这些问题你能用自己的话讲明白答辩这张卷子就已经及格了。最后再分享一个小技巧:项目代码之外花两天时间整理一份完整的项目部署文档和演示脚本。包括怎么初始化数据库、怎么打包运行、演示的流程顺序、关键功能点的操作路径甚至预设好每次演示用哪个账号登录、先点哪个按钮、输入什么搜索词。很多毕设项目不是毁在功能上而是毁在演示时找不到页面、数据没初始化、操作顺序混乱上。做好这些准备工作你的成果展示会比本身就做得好的系统更出彩。
返回列表