药品信息管理系统毕业设计全解析:从源码到部署,一套方案直接通关
又到一年毕业设计季,最近后台收到不少同学的私信,问药品信息管理系统这类题目该怎么做、怎么部署、答辩要准备哪些东西。这类"xx信息管理系统"确实是毕业设计里的常青树,但恰恰是这种经典题目,最容易踩坑——功能做得太浅会被老师质疑工作量不够,做得太全又容易陷入代码泥潭无法按期交付,更别提还有部署环境、数据库配置这一堆看着简单实则折腾人的事情。
这次我拿一个完整的药品信息管理系统项目来做个拆解,从技术选型、数据库设计、核心功能实现,到本地部署、常见坑排查,一条龙讲清楚,保证你照着做就能跑起来,答辨也有东西可讲。
1. 项目整体设计与技术选型解读
1.1 为什么这种题目都选Spring Boot + MySQL
药品信息管理系统本质上是一套标准的Web CRUD应用,核心是围绕"药品"这个实体做增删改查,再叠加用户权限、分类管理、供应商管理、库存预警等模块。这类系统大多数学校要求用Java技术栈,而现在业界的标准答案基本就是Spring Boot + MySQL。
先说Spring Boot。你去翻最近几届学长学姐的代码,十有八九是Spring Boot,原因很现实:Spring Boot把Spring那一大堆XML配置全部自动化了,内嵌Tomcat可以直接跑,搭配Maven管理依赖,一个命令就能启动项目,这对毕业设计来说太友好了。你不需要纠结Spring MVC那套复杂的配置流程,也不用考虑独立部署Tomcat,写代码的时间可以全部花在业务逻辑上,而不是环境配置上。
数据库这边,MySQL的优势不用多说了。轻量、开源、文档多,遇到什么问题网上一搜全有答案。药品管理系统这种量级的数据,连MySQL 5.7都能轻松扛住,更别说8.0了。Oracle虽然也常见于这类题目,但我个人不太建议毕业设计用——安装包太大,配置繁琐,本机跑起来占资源,答辩演示时万一卡顿影响心态。
至于前端,目前主流有两种做法,一种是直接用Thymeleaf模板引擎渲染服务端页面,配合Bootstrap做样式;另一种是前后端分离,Vue + Element UI调用后端接口。考虑到毕业设计的周期和答辩要求,我个人更推荐前者——代码量少,逻辑集中在后端,好讲也好答辨。如果选了前后端分离,你还得处理跨域问题,前端部署也要单独搞定,难度和工作量都会上去。当然,如果你对Vue很熟,可以提升一下项目的B格,这个后面单独说。
1.2 功能模块划分必须对得起"系统"两个字
很多同学做这类管理系统,容易做成只有一张表、只有增删改查的"玩具"。这种项目初筛还行,答辩时老师随便问两个问题就露馅了。药品信息管理系统要想有说服力,功能上至少要覆盖以下这些:
- 用户登录与权限控制:普通用户只能做查询和录入,管理员可以配置字典数据、管理用户账号
- 药品基本信息管理:药品名称、规格、生产厂家、批准文号、有效期、库存数量、进货价格、销售价格等字段的增删改查
- 药品分类管理:化学药、中成药、生物制品等分类的维护,一般做成下拉选数据字典
- 供应商管理:记录供应商名称、联系方式、供货资质等,在做进货操作时关联选择
- 入库与出库管理:药品入库单、销售出库单,自动更新库存
- 库存预警:库存低于阈值时高亮提示,这块是加分项,老师特别爱问
- 销售记录查询:按时间范围、药品名称等条件筛选销售记录
- 数据看板:首页展示药品总数、库存总量、过期药品数量、近期销售额等核心指标
模块划分的逻辑是"基础档案-业务流转-统计分析"层层递进。基础档案就是药品、分类、供应商、用户这些明细数据,业务流转是入库出库产生的记录对库存的反向影响,统计分析是基于业务数据做聚合展示。这个逻辑讲清楚后,答辩时你就能很清晰地回答"你这个系统的核心业务是什么、数据怎么流转的"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库设计与核心表结构解析
2.1 表结构设计:先画ER图再建表
数据库是整个系统的地基,很多同学赶进度直接上手建表,结果表建完才发现缺关联字段、缺唯一约束,返工成本极高。正确做法是先画ER图,再转成SQL脚本,最后才是写代码。
药品信息管理系统正常情况下核心表至少要有7张,我这里列出各表的用途和关键字段设计,你建表时对着来就好。
| 表名 | 用途 | 关键字段 |
|---|---|---|
| sys_user | 系统用户表 | id, username, password, real_name, role, status |
| drug_category | 药品分类表 | id, category_name, category_code, remark |
| drug_info | 药品信息表 | id, drug_name, category_id, specification, manufacturer, approval_number, unit, purchase_price, sale_price, stock_quantity, warn_quantity, expiration_date, status |
| supplier | 供应商表 | id, supplier_name, contact_person, contact_phone, address, remark |
| drug_inbound | 药品入库表 | id, drug_id, supplier_id, inbound_quantity, inbound_price, inbound_time, operator |
| drug_outbound | 药品出库表 | id, drug_id, outbound_quantity, outbound_price, outbound_time, operator, customer_name |
| sale_record | 销售记录明细表 | id, outbound_id, drug_id, sale_quantity, sale_price, sale_amount, sale_time |
这里有两个容易忽略的点要特别提醒。第一是drug_info表里必须有warn_quantity(预警数量)这个字段,它配合stock_quantity就能实现库存预警,没有这个字段后面的预警功能在逻辑上就没有支撑。第二是金额字段建议用DECIMAL类型而不是FLOAT或DOUBLE,因为浮点类型会出现精度缺失,比如0.1+0.2算出来不是0.3,药品价格这种对精度有要求的场景必须用DECIMAL(10,2)。
外键约束方面,我建表时会保留逻辑外键但不一定要加物理外键,使用JOIN查询时通过主键关联。这是因为物理外键在后期做数据维护、批量删除时,会带来不少麻烦(比如按分类删除时,该分类下还有药品就无法直接删,报外键约束错误)。这个设计决策在答辩时也可以作为一个小亮点讲一讲,显得你考虑问题比较全面。
2.2 SQL脚本的坑:字符集、存储引擎、初始数据
数据库脚本里最容易被忽略的是默认字符集和排序规则,特别是使用MySQL 8.0时,默认字符集虽然已经改成了utf8mb4,但如果你用的图形化工具(比如Navicat)直接手动建表,很可能沿用工具的默认配置,导致中文写入时出现乱码。建议每次都显式指定:
sql复制CREATE TABLE drug_info (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
drug_name VARCHAR(100) NOT NULL COMMENT '药品名称',
category_id BIGINT NOT NULL COMMENT '分类ID',
specification VARCHAR(100) COMMENT '规格',
manufacturer VARCHAR(200) COMMENT '生产厂家',
approval_number VARCHAR(100) COMMENT '批准文号',
unit VARCHAR(20) COMMENT '单位',
purchase_price DECIMAL(10,2) COMMENT '进货价',
sale_price DECIMAL(10,2) COMMENT '销售价',
stock_quantity INT DEFAULT 0 COMMENT '库存数量',
warn_quantity INT DEFAULT 10 COMMENT '库存预警值',
expiration_date DATE COMMENT '有效期至',
status TINYINT DEFAULT 1 COMMENT '状态:1启用 0停用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='药品信息表';
这里把很多细节都写在了注释里,建表时保留COMMENT是很好的习惯,一方面后面写代码时能快速看到字段含义,另一方面答辩时老师看数据库设计也比较直观。
初始化数据一定要给足。药品分类至少要建5条以上,药品数据要超过20条,用户表要有一个admin账号。演示的时候如果页面上空空荡荡,效果会大打折扣。有些同学交的SQL文件只有表结构没有insert数据,这会导致老师运行项目后什么都看不到,非常尴尬。
3. 部署与运行:从零到一跑通项目
3.1 环境准备清单与版本匹配
部署环境的版本匹配是新手最容易栽跟头的地方,很多同学项目跑不起来,不是因为代码有问题,而是版本对不上。以我这个基于Spring Boot 2.7.x的项目为例,推荐的环境版本如下:
| 组件 | 推荐版本 | 备注 |
|---|---|---|
| JDK | 1.8(Java 8) | Spring Boot 2.x搭配Java 8最稳 |
| Maven | 3.6.3或3.8.x | 不要用最新版,兼容性问题少 |
| MySQL | 5.7或8.0 | 8.0记得修改连接驱动 |
| IDEA | 2021.x或更高 | 社区版也能用 |
| Navicat | 任意版本 | 数据库管理工具 |
JDK和Maven的安装这里不多说,但有一个细节需要提醒:如果你用的是Mac M系列芯片,JDK需要注意选temurin或zulu的ARM版本,有些老版本的JDK在ARM架构下会闪退或性能异常。
数据库连接这块,Spring Boot 2.7.x默认使用mysql-connector-java 8.0.x,驱动类名是com.mysql.cj.jdbc.Driver。如果你的项目还是老写法com.mysql.jdbc.Driver,连接时会报警告甚至报错。在application.yml或application.properties里这样配置:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/drug_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: none
show-sql: true
注意url参数里必须带serverTimezone=Asia/Shanghai,否则会因为时区问题报错。useSSL=false是本地开发建议,避免SSL握手带来的额外开销和报警日志。
3.2 导入项目和初始化数据库的完整流程
整个过程我拆解成六步,每一步都有对应操作,建议你边看边操作。
第一步,创建数据库。打开Navicat或者命令行工具,执行连接数据库创建语句:
sql复制CREATE DATABASE drug_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
第二步,导入SQL脚本。拿到项目自带的drug_db.sql文件,选中drug_db数据库,右键"运行SQL文件",选择这个脚本执行。执行完成后刷新数据库,应该能看到所有表和数据都导入成功了。
这里我要多提醒一句:导入SQL脚本之前,先确认脚本文件本身就是utf8编码,如果文件是GBK编码,中文注释会乱码。Navicat导入时右下角有编码选择,记得选UTF-8。
第三步,用IDEA打开项目。选择File -> Open,选中项目根目录的pom.xml文件,IDEA会自动识别为Maven项目并下载依赖。第一次打开时Maven需要下载大量依赖,国内网络环境建议配一下阿里云镜像,否则可能等很久甚至失败。
在Maven的settings.xml中添加镜像:
xml复制<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>Aliyun Maven Mirror</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
第四步,修改数据库连接配置。打开src/main/resources下的application.yml(或application.properties),把username和password改成你自己的数据库账号密码。这一小步看着不起眼,但90%的启动失败都发生在这里,请务必核对。
第五步,启动项目。找到主类DrugManagementApplication.java,右键Run。看到日志输出Tomcat started on port(s): 8080就说明启动成功了。Spring Boot默认端口是8080,如果被占用,可以在配置里加server.port: 8081改掉。
第六步,浏览器访问。打开浏览器输入http://localhost:8080,正常会跳转到登录页面,用admin账号登录后就能看到系统主页了。如果页面显示404或连接失败,按照第六节里的排查方法处理。
3.3 本地端口和防火墙注意事项
端口问题也是新手常碰到的坑。启动时报端口占用,最常见的是之前启动过一次程序没关干净,或者被其他程序占用了8080端口。命令行执行netstat -ano | findstr 8080查看哪个进程占用了端口,然后到任务管理器里结束对应进程,或者直接在配置里改端口更省事。
如果你是在寝室或者实验室部署,其他同学要通过你的IP访问项目,比如局域网内演示,那就需要把后端服务的端口(默认8080)加入防火墙例外。Windows系统在"防火墙高级设置"里新增入站规则,允许TCP端口8080,这样别人才能通过http://你的IP:8080访问。演示时手机连同一个WiFi,也能直接访问,效果很酷。如果你自己电脑上装了安全卫士之类的软件,可能也需要额外允许端口。
注意:这个局域网访问方式,仅适用于演示时临时使用,千万别认为"我能在自己电脑上跑起来就完事了",实际检查环境时老师用的可能是他自己的电脑,所以完整的环境配置和部署流程,你必须自己心里有数、手上能复现。
4. 核心业务代码实现思路
4.1 药品CRUD如何做到优雅干练
药品信息的增删改查是系统最核心的部分,也是答辩时老师最容易深挖的地方。虽然增删改查听起来简单,但代码结构是否清晰、健壮性是否足够,在代码审查时一眼就能看出来。
接下来让你看看一个CRUD最基本的Controller写起来怎么实现,这里建议先写Service层接口和实现类,再写Controller:
java复制@RestController
@RequestMapping("/api/drug")
public class DrugInfoController {
@Resource
private DrugInfoService drugInfoService;
@GetMapping("/list")
public Result pageQuery(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
String keyword) {
PageInfo<DrugInfoVO> pageInfo = drugInfoService.queryPage(pageNum, pageSize, keyword);
return Result.success(pageInfo);
}
@PostMapping
public Result add(@RequestBody @Valid DrugInfo drugInfo) {
drugInfoService.add(drugInfo);
return Result.success();
}
@PutMapping
public Result update(@RequestBody @Valid DrugInfo drugInfo) {
drugInfoService.update(drugInfo);
return Result.success();
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Long id) {
drugInfoService.delete(id);
return Result.success();
}
}
这段代码里有几个值得关注的设计点。第一,用了Result统一返回结构,前端拿到后通过code判断是否成功,而不是裸返回实体对象,这样异常处理更规范。第二,分页参数用了PageInfo封装,MyBatis自带的分页插件PageHelper可以自动执行limit查询。第三,@Valid注解配合实体类字段上的校验注解(比如@NotBlank、@NotNull),在入口层就拦截非法参数,减少了Service层的判断逻辑。
再说说删除这里藏着一个细节问题:如果直接删药品,但入库记录、出库记录里引用了这个药品,那这些关联数据就成了"孤儿数据",对账时完全对不上。所以我做删除时,先判断该药品是否有关联的入库、出库记录,有的话就拒绝删除或者改成逻辑删除(status置为0)。逻辑删除的做法是,业务上这个药品不再显示在列表,但历史记录仍然保留,这更符合药品管理的业务实际。
4.2 入库出库与库存联动这个核心逻辑
药品管理系统最关键的业务链路是"入库/出库自动更新库存",这个逻辑在答辩时几乎必问。实现思路很简单——在事务内先更新库存表字段,再插入操作记录表,两个操作要么同时成功要么同时失败,这就是数据库事务的原子性保证。
java复制@Transactional(rollbackFor = Exception.class)
public void inbound(DrugInboundDTO dto) {
DrugInbound inbound = new DrugInbound();
inbound.setDrugId(dto.getDrugId());
inbound.setSupplierId(dto.getSupplierId());
inbound.setInboundQuantity(dto.getQuantity());
inbound.setInboundPrice(dto.getPrice());
inbound.setOperator(SecurityUtils.getCurrentUsername());
drugInboundMapper.insert(inbound);
DrugInfo drugInfo = drugInfoMapper.selectById(dto.getDrugId());
drugInfo.setStockQuantity(drugInfo.getStockQuantity() + dto.getQuantity());
drugInfoMapper.updateById(drugInfo);
}
这里@Transactional注解是必须加的,它保证如果后面的库存更新抛异常,前面的入库记录插入也会回滚,不会出现入库单有记录但库存没更新这种数据不一致。这个点你在答辩时主动说出来,老师会觉得你对事务有理解。
出库的逻辑同样,库存扣减前还要判断当前库存是否足够。如果库存不够要抛出业务异常,提示"库存不足",而不是等库存变成负数。这个判断用SQL直接判断会更严谨:update drug_info set stock_quantity = stock_quantity - #{quantity} where id = #{drugId} and stock_quantity >= #{quantity},受影响行数为0就说明库存不足。这种写法在高并发场景下可以避免超卖问题,毕业设计虽然不会真有人并发抢药,但把这个方案讲出来是加分项。
4.3 库存预警列表和统计报表怎么实现
库存预警就是查询所有库存低于预警值的药品,SQL一句话就能搞定:
sql复制SELECT * FROM drug_info WHERE stock_quantity < warn_quantity AND status = 1;
前端展示时,给低于预警值的药品行加上红色背景高亮。更进一步,可以在首页的看板里加一个"预警药品数量"的统计卡片,以及一条预警药品列表,这样一进入系统就能直观感受到这个功能的存在,老师演示时也能一眼看到效果。
统计报表部分,如果只是简单的计数,可以用count、sum函数聚合。比如统计药品总数、库存总量、分类数量:
sql复制SELECT COUNT(*) AS total_drugs, SUM(stock_quantity) AS total_stock FROM drug_info WHERE status = 1;
如果想让首页看板展示近7天的销售趋势折线图,可以用日期分组统计每天销售总额。数据库查询按DATE_FORMAT(sale_time, '%Y-%m-%d')分组,然后传给前端图表库渲染。前端图表我习惯用ECharts,一个div + 几行配置就能画出漂亮的折线图,比自己写canvas强太多。
5. 项目结构与核心前后端交互解析
5.1 后端分包结构不要乱来
分包结构虽然不影响功能,但影响阅读体验和代码规范分。我对这类老项目的分包建议是:
code复制com.example.drug
├── controller // 控制层,接收请求参数,调用service
├── service // 业务层接口
│ └── impl // 业务层实现
├── mapper // MyBatis持久层接口(如果用了mybatis-plus就是mapper)
├── entity // 数据库实体类
├── dto // 前端传参对象(入参DTO)
├── vo // 前端展示对象(出参VO)
├── config // 配置类(拦截器、CORS、全局异常)
├── common // 通用类(Result、异常处理、常量)
└── utils // 工具类
各层之间遵循"Controller不写SQL、Service不写请求参数校验、Mapper只操作数据库"的原则。刚入门时容易有人把业务逻辑全写在Controller里,代码虽然也能跑,但后来维护时会很痛苦。答辩时老师也会看你代码结构,分清楚各层职责是很基础的工程素养。
5.2 前端页面组织与核心交互
如果你用Thymeleaf,页面结构一般是templates目录下按模块建文件夹,比如login.html放根目录,drug/、inbound/、outbound/目录放各模块的页面。公共部分(侧边栏、顶部栏)可以用th:fragment抽取,避免每个页面重复粘贴。
前端和后端交互,我习惯用jQuery的$.ajax或$.post发送请求,返回JSON数据后动态渲染表格。标准的写法是:
javascript复制$.ajax({
url: '/api/drug/list',
type: 'GET',
data: { pageNum: 1, pageSize: 10, keyword: $('#keyword').val() },
success: function (res) {
if (res.code === 200) {
// 渲染表格
renderTable(res.data.list);
} else {
alert(res.msg);
}
},
error: function () {
alert('网络请求异常');
}
});
这种交互方式不需要引入完整的前端框架,在你想要快速交付毕业设计的时候是最顺手的方案。唯一需要注意的有两点,一是所有ajax请求的success回调里必须判断res.code,不能默认认为请求成功就代表业务成功;二是服务端接口的权限拦截(比如未登录用户不能调用接口)要处理好,不然直接访问接口地址就绕过了登录,这在答辩时是硬伤。
5.3 登录权限这样设计才完整
登录权限是毕业设计里最容易被学生做坏的地方。最基础的做法是登录成功后把用户信息存到Session里,然后写一个拦截器,对所有非登录接口做校验:
java复制public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null) {
// 未登录,跳转到登录页
response.sendRedirect("/login");
return false;
}
return true;
}
}
然后在WebMvcConfig里注册拦截器,设置排除登录页、静态资源的路径:
java复制@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/login.html", "/css/**", "/js/**", "/images/**", "/api/user/login");
}
再进阶一点,可以给用户分角色(管理员、普通操作员),管理员可以访问用户管理页面,普通操作员只能做业务录入和查询。这一步可以用HandlerInterceptor里再判断一下role字段实现。如果你想把这块做扎实,可以引入Spring Security或Sa-Token,但毕业设计我建议不要搞太复杂,一个拦截器+Session足够了,代码量小、好讲清楚、排查问题也简单。
6. 常见问题排查与避坑指南
6.1 启动报错与页面404速查表
我从过往答疑中筛选出下面这些高频问题,整理成一张速查表,你运行时遇到类似的问题直接对照处理。
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
| Access denied for user 'root'@'localhost' | 数据库用户名或密码填错 | 检查application.yml中的账号密码 |
| Unknown database 'drug_db' | 数据库没创建或数据库名不一致 | 执行CREATE DATABASE语句 |
| Table 'drug_db.drug_info' doesn't exist | SQL脚本没导入或导错库 | 重新执行SQL脚本,确认选中正确的库 |
| java.sql.SQLException: The server time zone value... | 时区未设置 | 在jdbc url加serverTimezone=Asia/Shanghai |
| Port 8080 was already in use | 端口被占用 | 结束占用进程或修改server.port |
| 页面访问出现Whitelabel Error Page | 接口路径写错或Controller映射错误 | 核对接口地址、类上@RequestMapping注解 |
| 前端ajax请求404 | 请求URL错误或方法类型不匹配 | 检查Controller的GET/POST注解,检查context-path |
| 启动后中文乱码 | 文件编码不一致 | IDEA右下角设置UTF-8,数据库连接url加characterEncoding=utf8 |
6.2 数据库中文乱码问题全链路排查
中文乱码是最磨人的一个问题,它可能出现在四个环节:数据库连接、数据库本身字符集、表字符集、页面显示。排查时按这个顺序来。
首先确认数据库和表的字符集,执行:
sql复制SHOW CREATE TABLE drug_info;
看到DEFAULT CHARSET=utf8mb4就说明没问题,如果是latin1就说明建表时字符集选错了,需要重建表或者ALTER TABLE修改字符集。
其次确认连接url是否带characterEncoding=utf8。然后确认IDEA文件编码,右下角点击编码方式,把项目所有文件都切到UTF-8。最后确认前端页面的contentType和meta标签是否声明了utf-8。
如果你在向数据库中插入中文时出现"Incorrect string value"错误,那一定是表的字段字符集不对,执行修改字段字符集语句:
sql复制ALTER TABLE drug_info CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
四步走完,基本能解决95%以上的乱码问题。
6.3 答辩前必看的三个演示翻车点
很多学生程序跑得好好的,一到答辩现场就翻车。我总结几个最常见的现场事故,给你提个醒。
第一,演示时临时改数据库密码。有些同学答辩前一天把自己的本地MySQL密码改了,或者换了一台电脑忘了改配置。建议答辩当天早上先完整跑一遍系统,确认能登录、能查询、能新增数据。
第二,演示数据太少看不出效果。如果数据库中只有三五条药品记录,做分页和搜索时页面空空荡荡,体验很差。答辩前多造一些测试数据,最少准备30条以上药品、十几条出入库记录。
第三,网络不好导致前端资源加载缓慢。如果你用了CDN引入Bootstrap、jQuery,而答辩现场网络不稳定,页面样式会全面崩坏。最稳妥的办法是把这些静态资源下载到本地static目录,确保断网也能正常展示。这条建议是绝对值得遵循的实操经验。
7. 二次开发扩展方向和避坑心得
7.1 四个值得加的进阶功能
基础功能做完、能跑通以后,如果时间和精力允许,我建议加点进阶功能,不仅能让项目看起来更有工作量,还能在答辩时多几个讲头。
药品过期提醒是第一个建议。给drug_info表的expiration_date字段加个查询,统计90天内即将过期的药品,在列表页和首页都展示出来。过期药品在真实药房场景中是需要特别关注的,做了这个功能,业务逻辑的完整性就上去了。
批量导入导出也值得做。用EasyExcel或者POI实现药品信息的Excel批量导入、导出。这个功能在实际场景中非常实用,而且能体现你对第三方工具库的使用能力。
操作日志审计是第三建议。用AOP做个切面,拦截所有Controller请求,记录操作人、操作时间、操作内容。这功能逻辑不复杂,但对系统的完整度提升非常大。
最后是单元测试覆盖。给Service层的核心方法(比如入库、出库)写几个单元测试,答辩时展示测试用例,这在很多评审老师眼里是重要的加分项。哪怕只写几个关键的测试,也说明你有测试意识。
7.2 代码运行不起来的时候,先深呼吸再看这三处
代码跑不起来时,大多数人第一反应是重新看代码、到处改,其实大多数问题出在配置而不是代码逻辑。
先看控制台第一条异常堆栈,定位到具体的类和行号,大部分问题都能从报错信息里看到端倪。再看数据库连接配置,把账号、密码、数据库名这三点逐一核对,这三样错了控制台报Access denied或Unknown database是最常见的。最后看Maven依赖是否导入完整,如果IDE里显示找不到某个类,在Maven面板点一下刷新,让依赖重新下载,排除因下载中断导致的jar包缺失。
如果以上三步还解决不了,最简单的办法是把完整报错信息复制到搜索引擎里搜一下,绝大多数问题都能找到现成解决方案。不要怕报错,会看报错、会查资料,比背代码重要得多。
7.3 写在最后:关于这类项目的一些大实话
药品信息管理系统这类题目,代码量不算大,技术难度也不算高,它真正的价值在于让你把大学四年学的东西串起来:数据库设计、后端开发、前端交互、项目部署。做完一个完整的项目,你会对这些知识有全新的理解,这是看十遍教材都换不来的。
我自己的体会是,做这类项目最怕的不是技术不会,而是眼高手低,一开始就想搞微服务、搞Redis、搞前后端分离,结果越搞越复杂,最后连基础功能都没做完。选一套顺手的技术,把核心逻辑做扎实,把部署流程跑通,这个项目就已经超过了大多数同龄人。
最后再分享一个小技巧:项目快完工时,把整个部署过程写成一个文档,从创建数据库到启动项目的每一步都用截图记录下来。答辩时可以展示这个文档,同时也能帮助未来的自己快速恢复项目环境。这个习惯我一直保留到现在,工作中写技术方案、交接文档时真的会感谢当年的自己。
