☰
SpringBoot+Vue医药管理系统:从零搭建到部署的完整毕设指南
2026/10/19 10:51:11 网站建设 项目流程

做毕设选管理系统方向的人很多,但能把“医药管理系统”做完整、做得像一个能交付的软件而不是“为了答辩凑功能的Demo”,其实不多。这个SpringBoot+Vue的医药管理系统平台,恰好是市面上比较标准的一套Java Web毕设案例:后端用SpringBoot,前端用Vue,数据库脚本和接口文档都齐了,拿到之后能快速跑起来,也能根据自己理解改造成答辩加分项。

这个项目的核心价值在于,它不只是“能增删改查”,而是围绕医药行业真实的业务流程来设计的:药品基础资料维护、分类管理、供应商管理、采购入库、销售开单、库存预警、临期药品提醒、会员管理,再加上后端的用户登录和权限控制。它适合正在准备Java Web相关的毕业设计、课程设计,或者想快速上手SpringBoot+Vue前后端分离项目的同学参考。如果你只想找一份能直接抄的代码,这篇内容能帮你少走弯路;如果你想把项目吃透,在答辩时能讲清楚每一个表、每一个接口为什么这么设计,这篇更值得看完。

我接下来就按完整的项目拆解顺序,从功能设计、技术选型、数据库脚本、后端接口、前端联调、部署运行到问题排查,把整套项目的思路和实操细节完整过一遍。

1. 项目整体设计与功能拆解

1.1 医药管理系统到底要管什么

很多同学一听到“医药管理系统”,第一反应就是药品的增删改查,其实这是把业务想简单了。药店或者医院药房的信息化管理,核心不只是“记录药品”,而是围绕药品的“进销存”三个字展开的——进就是采购入库,销就是销售出库,存就是库存管理。再加上药品这个特殊商品有批次、有效期、批准文号这些属性,系统要比普通的商品管理系统多考虑一层安全合规因素。

这套项目里的功能设计,基本上是按照真实药房业务流程来的。系统面向两类用户:管理员和收银员。管理员负责药品维护、供应商管理、采购入库、用户管理这些比较高权限的操作;收银员主要负责销售开单、会员录入、查询库存这类日常操作。权限不一样,前端菜单不一样,后端接口也要做对应的拦截,这种设计在毕业答辩里很加分,因为老师问到“你怎么考虑用户权限的”,至少能拿出实际的实现方案。

整个系统的业务闭环是这样的:管理员先维护好药品分类和药品信息,录入供应商,然后做采购入库,库存增加;收银员在销售页面根据药品名称或编号检索药品,加购、结算、生成销售单,库存自动扣减;库存低于阈值时系统预警,药品临近有效期时也要能提醒。这个闭环跑通了,这个项目才叫“管理系统”,而不是“管理页面”。

1.2 模块划分与角色权限设计

从代码层面拆开看,这个项目包含以下功能模块:

  • 系统管理:用户登录、退出登录、用户管理、修改密码、角色权限控制。
  • 药品管理:药品分类管理、药品信息管理(商品名、通用名、规格、剂型、生产厂家、批准文号等)。
  • 库存管理:库存查询、库存预警、临期药品列表、库存盘点辅助逻辑。
  • 采购管理:供应商管理、采购入库单录入、采购记录查询。
  • 销售管理:销售开单(购物车式)、销售记录查询、销售明细查看。
  • 会员管理:会员信息维护、会员价设置、购药记录查看。

角色权限这块,比较简洁的做法是基于JWT + 拦截器或Spring Security来实现。其实毕设项目用Spring Security有时候反而会增加学习成本,如果只是为了实现“不同角色看到不同菜单、访问不同接口”,用拦截器校验token和角色也完全够用。我见过不少项目的代码里其实没有真正的权限表,只是在Controller层写死了“管理员角色才能调用”,这种实现虽然简单,但答辩时容易暴露问题。如果时间充裕,建议把用户表、角色表、用户角色关系表三张表都建上,哪怕不用前端动态菜单,也能体现设计思路是完整的。

2. 技术选型背后的逻辑

2.1 为什么是SpringBoot + Vue这套组合

先直接说结论:SpringBoot + Vue已经是目前Java Web毕设的事实标准,你拿这套组合去答辩,老师基本不会在技术选型上挑毛病。原因是这套组合的开发效率和演示效果都比较好。

SpringBoot这边,内置Tomcat,不用单独部署容器就能跑起来;自动配置机制把SpringMVC、MyBatis、数据源这些集成工作简化了一大截;再加上它和Spring生态天然配合,写Service、Controller时不用写一堆XML配置。对毕设来说,最重要的是“能跑起来”和“不太折腾”,SpringBoot在这方面确实比传统的SSH、SSM框架省心不少。

Vue这边,核心价值在于前后端分离的开发模式清晰:前端只管渲染页面和调用接口,后端只管提供数据和业务逻辑。用Vue CLI或Vite创建工程,配合Element UI/Element Plus组件库,开发后台管理系统的效率非常高——表格、表单、弹窗、分页这些常见的后台组件都是现成的。你不需要自己手写一大堆CSS去拼页面,又能做出比JSP页面好看很多的界面。

还有一个现实原因:这套组合的资料多。无论是你代码里碰到一个bug,还是答辩时老师问到一个概念,网上随便一搜就是大把的解决方案和讲解,对于毕设周期很短的场景来说,这个“后援力量”很重要。

2.2 后端核心依赖与版本选择

后端项目的pom.xml里,核心依赖大概有这些:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>

这里有一个比较关键的版本坑:如果你的SpringBoot版本是2.6.x以上,配Swagger的时候要注意,springfox的兼容性有点问题,启动时会报Failed to start bean 'documentationPluginsBootstrapper'。解决办法有两个,一是把SpringBoot版本降到2.5.x,二是换成springdoc-openapi这套方案。从实际体验来说,springdoc-openapi的配置更省心,依赖也更简洁:

<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.7.0</version> </dependency>

MyBatis-Plus选型也是为了省事。单表CRUD不需要手写SQL,BaseMapper里已经提供了selectById、selectPage、insert、updateById等方法;分页查询用PaginationInterceptor配置一下就能直接拿Page对象;条件查询用LambdaQueryWrapper,比如根据药品名模糊查询,代码就一行,不用在XML里写<where><if>这种标签。对于自己做毕设的人来说,这个选择能省下至少一天的编码时间。

2.3 前端环境和依赖选择

前端工程一般用Vue CLI创建(如果你熟悉Vite,用Vite也可以,但毕设资料大多数还是Vue CLI的写法,建议跟主流保持一致)。需要装的依赖包括:

  • vue-router:前端路由管理。
  • axios:发送HTTP请求。
  • element-ui(Vue 2项目)或 element-plus(Vue 3项目)。
  • sass:如果你需要自定义样式,可以一起装上。

如果项目是基于Vue 3 + Element Plus,那么main.js里大概是这样:

import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' import router from './router' const app = createApp(App) app.use(router) app.use(ElementPlus) app.mount('#app')

这里有个小提醒:Vue 2和Vue 3的API差别很大,如果你拿到的是Vue 2写的项目,又用Element Plus去适配,那基本改不动。建议先看清package.json里的vue版本,再做对应调整。这个项目如果用Vue 2,对应的UI组件库就是Element UI,配的是Vue 2的Options API写法;用Vue 3的话,配的则是组合式API或者还是选项式API都能兼容。

3. SQL脚本设计与数据库初始化

3.1 表结构设计这关怎么过

数据库是一个管理系统最核心的部分。医药管理系统的表结构,不是只建一张“药品表”就能支撑起来的。合理的设计至少包含八到十张表,我在这个项目里见到的整理如下:

表名用途关键字段
sys_user系统用户id, username, password, real_name, role, status
drug_category药品分类id, category_name, parent_id, sort_order
drug_info药品信息id, drug_code, generic_name, trade_name, spec, unit, manufacturer, approval_number, retail_price, stock_quantity, stock_low_limit, expire_date
supplier供应商id, supplier_name, contact_person, phone, address, status
purchase_order采购入库单id, order_no, supplier_id, total_amount, create_time, create_by
purchase_order_detail采购入库明细id, order_id, drug_id, purchase_price, quantity, sub_total
sale_order销售单id, order_no, member_id, total_amount, pay_type, create_time, create_by
sale_order_detail销售明细id, order_id, drug_id, sale_price, quantity, sub_total
member会员信息id, member_no, name, phone, level, discount
sys_role_permission角色权限关联(可选)id, role, permission_key

药品表是核心表,设计时要特别注意几个字段:stock_low_limit是库存预警阈值,低于这个值就触发低库存预警;expire_date是有效期,取当前日期在30天内的记录就是临期药品。这两个字段在前端界面上会有对应的“库存预警”和“临期提醒”,代码里是查询条件,所以数据库设计时一定要留出来。

还有一点,金额字段建议用DECIMAL(10,2),不要用DOUBLE或者FLOAT,因为浮点数在计算金额时存在精度问题,比如0.1+0.2不等于0.3,这在财务场景里是不可接受的。销售单和采购单的关联方式是用“主表+明细表”的结构,主表记录单据总体信息,明细表记录每一条药品的进货或销售数据,这样既能查出一张订单买了什么,也能反查某个药品被哪些订单采购或销售过。

3.2 SQL脚本执行与编码避坑指南

拿到SQL脚本后,第一件事是看数据库版本。MySQL 5.7和8.0在语法上大部分兼容,但如果是8.0,驱动连接用的是com.mysql.cj.jdbc.Driver,URL里要加时区参数;如果是5.7,用旧的驱动也可以。

执行脚本时最容易碰到两个问题:中文乱码和脚本执行报错。中文乱码的原因通常是SQL文件本身是UTF-8编码,但连接数据库时客户端字符集不对。解决办法是在连接URL上加参数:

jdbc:mysql://localhost:3306/medicine?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

有人喜欢在URL里加useSSL=false,这是因为本地开发环境没有配置SSL证书,连接时MySQL会给出警告。虽然不影响运行,但加了这个参数后控制台更干净,也能避免在特定MySQL版本下连接失败。

执行SQL脚本有几种方式。如果你用的是Navicat,直接右键数据库,选择“运行SQL文件”,选好脚本文件,点开始就能执行;如果你用命令行,则要这样操作:

mysql -u root -p medicine < medicine.sql

这条命令的前提是数据库medicine已经创建好。如果没有,可以先登录MySQL:

CREATE DATABASE medicine DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE medicine; SOURCE /path/to/medicine.sql;

这里我建议用utf8mb4而不是utf8,原因很简单:utf8mb4是utf8的超集,可以存储emoji和一些生僻字。虽然系统里不一定用得到emoji,但从规范角度来说,建库用utf8mb4是一个更好的习惯。

另外一个常见坑是脚本重复执行。如果脚本里已经包含DROP TABLE语句,重复执行没问题;但很多脚本没有加DROP,第二次执行时会报“Table already exists”。这不算报错,但是会打断执行流程。我的习惯是:在脚本最前面加上DROP TABLE IF EXISTS,这样能保证脚本可以重复执行,开发和调试的时候省心很多。

4. 后端核心实现与接口设计

4.1 后端目录结构与启动流程

SpringBoot项目的包结构,建议按“controller / service / mapper / entity / common / config”来分包。这个项目也是这么做的:

src/main/java/com/medicine/ ├── common/ │ ├── Result.java(统一返回结果类) │ └── ResultCode.java(返回状态码枚举) ├── config/ │ ├── MybatisPlusConfig.java │ ├── WebConfig.java(跨域配置) │ ├── SwaggerConfig.java │ └── JwtInterceptor.java ├── controller/ │ ├── AuthController.java │ ├── DrugController.java │ ├── CategoryController.java │ ├── SupplierController.java │ ├── PurchaseController.java │ ├── SaleController.java │ └── MemberController.java ├── entity/ │ ├── DrugInfo.java │ ├── SaleOrder.java │ ├── ... ├── mapper/ │ ├── DrugInfoMapper.java │ └── ... ├── service/ │ ├── DrugService.java │ └── ... └── MedicineApplication.java

统一返回结果类Result很重要,它保证了前端拿到的数据格式是一致的。一般结构是:

public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(String message) { Result<T> result = new Result<>(); result.setCode(500); result.setMessage(message); return result; } }

前端axios拦截器里会根据code字段判断请求是否成功,是200就取data,否则弹出错误提示。这种统一封装的好处是,接口定义清晰,前端处理逻辑统一,不会出现一个接口返回{success:true},另一个返回{status:1}这种混乱情况。

启动项目的方式很简单:在MedicineApplication.java里右键Run,启动成功后SpringBoot会默认在8080端口启动Tomcat。如果你本地的8080端口被占用了,可以在application.yml里改:

server: port: 8080

然后访问http://localhost:8080/,如果项目配置了Swagger,地址是http://localhost:8080/swagger-ui/index.html(springdoc方案)或http://localhost:8080/swagger-ui.html(springfox方案)。

4.2 核心接口设计与Swagger接口文档

这个医药管理系统的接口设计,我按模块罗列一下核心接口:

登录认证模块:

  • POST /api/auth/login,参数为username和password,返回token。
  • POST /api/auth/logout,退出登录,清除token。

药品管理模块:

  • GET /api/drug/page,分页查询,支持按药品名称、分类、生产厂家筛选。
  • GET /api/drug/{id},药品详情。
  • POST /api/drug,新增药品。
  • PUT /api/drug,修改药品。
  • DELETE /api/drug/{id},删除药品。
  • GET /api/drug/expiring,查询临期药品。
  • GET /api/drug/lowStock,查询低库存药品。

采购管理模块:

  • GET /api/supplier/page,供应商分页查询。
  • POST /api/purchase,创建采购入库单(同时写入主表和明细表)。
  • GET /api/purchase/page,采购单分页查询。

销售管理模块:

  • POST /api/sale,创建销售单(购物车数据一次性提交)。
  • GET /api/sale/page,销售单分页查询。
  • GET /api/sale/detail/{orderId},销售单详情。

参与前后端分离项目,接口文档是必须有的。这个项目里采用Swagger自动生成接口文档,你不必为每个接口单独写Word文档,只要在Controller方法上加上注解,Swagger会自动扫描并生成可在线调试的文档页面。

示例:

@Api(tags = "药品管理接口") @RestController @RequestMapping("/api/drug") public class DrugController { @ApiOperation(value = "分页查询药品列表") @GetMapping("/page") public Result<Page<DrugInfo>> page(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(required = false) String drugName) { // 业务代码 } }

配置Swagger后,前端同学或者答辩老师可以直接在页面上调试接口,不需要额外导出文档。如果学校要求提交接口文档文档,也可以从Swagger的/v3/api-docs(springdoc)或/v2/api-docs(springfox)导出JSON或转换成Markdown/HTML格式。这个“导出接口文档”的操作,在毕设材料整理时很实用,能省掉不少手写文档的功夫。

4.3 登录鉴权与权限控制怎么做

登录鉴权直接决定了一个管理系统是否“真正完整”。这个项目用的是JWT方案,流程是这样的:

  1. 用户输入用户名密码,后端校验用户名是否存在、密码是否正确。
  2. 校验通过后,生成一个JWTS token,token里包含用户ID、用户名和角色。
  3. 把token返回给前端,前端存到localStorage或sessionStorage。
  4. 前端axios请求拦截器在每次请求的header里自动加上Authorization: Bearer <token>。
  5. 后端配置拦截器,拦截除/api/auth/login以外的所有请求,校验token是否有效、是否过期。

JWT工具类核心代码很简单:

public class JwtUtil { private static final String SECRET = "medicine-secret-key"; private static final long EXPIRE_TIME = 2 * 60 * 60 * 1000; // 2小时 public static String generateToken(String username, String role) { return Jwts.builder() .setSubject(username) .claim("role", role) .setExpiration(new Date(System.currentTimeMillis() + EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } }

拦截器注册:

@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getMethod().equals("OPTIONS")) { return true; } String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.setStatus(401); return false; } try { JwtUtil.parseToken(token.substring(7)); return true; } catch (Exception e) { response.setStatus(401); return false; } } }

注册拦截器时注意,要让静态资源和登录接口放行。这个细节如果忘记配置,会出现“前端页面能打开,但所有数据接口都401”的问题,而且排查要花不少时间。

权限控制如果要做得再细一点,可以在拦截器里解析token中的role字段,判断当前角色是否允许访问某个接口。比如:

if ("ADMIN".equals(role)) { return true; } if ("SALE".equals(role) && allowedForSaleUrls.contains(request.getRequestURI())) { return true; }

毕业答辩时,这个点是最值得深入讲的。因为“管理系统”和“普通的CRUD页面”之间最大的区别,就是有没有真正的权限控制。很多项目虽然页面上根据角色隐藏了菜单,但后端接口不拦截,别人知道接口地址一样能调用,这在安全设计上是说不过去的。所以你哪怕只是做一个简单的拦截判断,也要让代码体现出“后端也有鉴权”这个意识。

5. 前端Vue实现细节与前后端联调

5.1 前端目录与页面结构规划

前端工程结构大概是这样的:

src/ ├── api/ │ ├── login.js │ ├── drug.js │ ├── sale.js │ ├── supplier.js │ └── request.js(axios封装) ├── router/ │ └── index.js ├── layout/ │ └── Layout.vue(主布局,包含侧边栏和顶部导航) ├── views/ │ ├── login/Login.vue │ ├── drug/DrugList.vue │ ├── drug/CategoryList.vue │ ├── purchase/PurchaseList.vue │ ├── purchase/PurchaseAdd.vue │ ├── sale/SaleList.vue │ ├── sale/SaleAdd.vue │ ├── supplier/SupplierList.vue │ └── member/MemberList.vue └── main.js

页面级别上,登录页是一个独立的页面;登录成功后进入Layout布局,左侧菜单显示模块列表,右侧是内容区,点击菜单切换对应的Vue组件。这个布局是所有后台管理系统的通用模式,在Vue项目里用路由嵌套来实现:Layout是父路由,各功能页面是子路由。

路由配置示例:

const routes = [ { path: '/login', component: Login }, { path: '/', component: Layout, redirect: '/drug', children: [ { path: 'drug', component: DrugList }, { path: 'purchase', component: PurchaseList }, { path: 'sale', component: SaleList }, ] } ]

路由守卫也要配一下,未登录的用户访问首页时自动跳转到登录页:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path !== '/login' && !token) { next('/login') } else { next() } })

这个小配置很实用,是“前端权限控制”的一部分,也值得在答辩时提一下。

5.2 axios封装与请求拦截器配置

前端和后端联调,最关键的是把axios封装做好。封装好了,后续每个接口都只需要写一行调用代码。

在request.js里统一配置axios实例:

import axios from 'axios' import { Message } from 'element-ui' import router from '@/router' const request = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器 request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }) // 响应拦截器 request.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { Message.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response && error.response.status === 401) { Message.error('登录已过期,请重新登录') localStorage.removeItem('token') router.push('/login') } else { Message.error('网络请求失败') } return Promise.reject(error) } ) export default request

然后每个API模块对应一个文件,比如api/drug.js:

import request from './request' export function getDrugPage(params) { return request({ url: '/drug/page', method: 'get', params }) } export function addDrug(data) { return request({ url: '/drug', method: 'post', data }) }

页面里只需要这样调用:

import { getDrugPage } from '@/api/drug' const res = await getDrugPage({ pageNum: 1, pageSize: 10, drugName: '阿莫西林' }) this.tableData = res.data.records

这里有一个开发阶段非常实用的配置:在项目根目录的vue.config.js里配置开发代理,避免跨域问题:

module.exports = { devServer: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

这样前端在3000端口访问/api/drug/page时,会被代理到后端的8080端口,浏览器层面不触发跨域,开发体验顺畅很多。如果不用代理,你可能要在后端写CORS配置,或者在前端用完整的路径请求然后挨个处理跨域报错,体验会差很多。

5.3 销售开单页面的核心逻辑

整个项目里技术含量最高的页面,我个人认为是销售开单页面。它不是一个简单的提交表单,而是带购物车逻辑的交互页面。

页面上需要实现:左侧是药品搜索列表(支持按名称或编号搜索,结果显示库存数量和零售价),点击“加入购物车”按钮把药品加入右侧的购物车区域;购物车里可以修改数量、删除单条记录;购物车实时计算总金额;点击“结算”按钮,把购物车数据一次性提交到后端。

前端购物车的核心数据结构就是一个数组:

cart: [ { drugId: 1, drugName: '阿莫西林胶囊', spec: '0.25g*24粒', price: 15.00, quantity: 2, subTotal: 30.00 }, { drugId: 2, drugName: '布洛芬缓释胶囊', spec: '0.3g*20粒', price: 18.50, quantity: 1, subTotal: 18.50 } ]

提交时,把主表字段(总金额、支付方式、收银员、会员ID)和明细数组一起发给后端。后端事务性地做两件事:插入销售单、扣减库存。这里一定要加事务,否则可能出现销售单录入了但库存没扣减的情况。SpringBoot里加事务很简单,Service方法上加上@Transactional注解即可。

这个页面的代码量不小,但非常能体现一个开发者对业务的理解。答辩时你可以主动说“这个页面处理了一个订单事务,库存扣减和销售单写入在同一个事务里,保证数据一致性”,老师的印象分会明显提升。

6. 部署运行与完整流程

6.1 本地运行完整步骤

拿到整套源码后,要按这个顺序跑起来:

第一步,准备基础环境:JDK 8或11,Maven 3.6+,Node.js 14+,MySQL 5.7或8.0,Navicat或命令行工具。这些缺一不可,先检查一下环境。

第二步,初始化数据库:用Navicat或命令行创建medicine数据库,执行项目提供SQL脚本,确保所有表创建成功。

第三步,修改数据库配置:打开后端项目的application.yml,改数据库地址、用户名、密码。

spring: datasource: url: jdbc:mysql://localhost:3306/medicine?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver

第四步,启动后端:在项目根目录执行mvn spring-boot:run,或者在IDEA里右键MedicineApplication运行。启动日志出现“Started MedicineApplication”就说明成功了。

第五步,启动前端:在前端目录执行:

npm install npm run serve

如果npm install很慢,可以切换淘宝镜像:

npm config set registry https://registry.npmmirror.com

启动之后,控制台会提示访问地址,一般是http://localhost:3000。

第六步,打开浏览器访问前端地址,使用管理员账号登录,开始操作。

6.2 本地运行常见问题速查表

我梳理一下实际操作中遇到频率比较高的问题,分成几类,方便你排查:

现象原因解决办法
后端启动报错,提示端口8080被占用本机其他程序占用端口改server.port,或关闭占用进程
数据库连接失败,Access denied数据库用户名或密码错误检查application.yml里的连接配置
数据库连接失败,Communications link failureMySQL没启动,或端口不是3306启动MySQL服务,或改URL里的端口
中文乱码数据库表字符集不是utf8mb4,或连接URL没有编码参数确认建库字符集,URL加characterEncoding=utf8
前端npm install报错,node-sass安装失败Node版本和node-sass不兼容换用dart-sass,或调整Node版本
前端页面能打开但接口401登录拦截器把请求拦截了,或token没带检查axios拦截器,检查后端放行的URL配置
接口报CORS错误前后端端口不同,没用代理用vue.config.js配置代理,或后端加跨域配置
Swagger页面无法访问springfox和SpringBoot版本不兼容换springdoc,或降SpringBoot版本
启动时提示Table doesn't existSQL脚本没执行成功重新执行脚本,确认所有表创建成功

还有一个容易被忽略的问题:如果你在后端新加了接口,前端调用时发现404,先排查Controller的@RequestMapping路径和前端API文件里请求的URL是否完全一致。大小写、多一个斜杠都不行,这类问题如果不仔细看,能找半天。

7. 从毕设到项目的进阶建议

如果你的目标不只是“让系统跑起来”,而是想把项目作为毕业设计的亮点,或者在面试时能拿出来讲,有几个方向值得投入时间。

第一,把库存预警和临期提醒做成真正的提醒维度,不只是列表查询。例如在导航栏右上角显示未处理预警数量,点击进入预警详情。这里需要思考“什么是预警”——库存低于阈值算一条,药品距离有效期不足30天算一条,接口可以用一个聚合统计接口实现,前端登录后定时拉取。这种“主动推送”的体验,比用户手动去查询列表要更像一个真正的系统。

第二,考虑多门店或者多仓库场景。现在的表结构只有单一库存字段,如果未来要支持多门店,就需要在药品表之外设一个“门店库存表”,每一条记录代表某个仓库中某个药品的库存数量。这个设计能体现出你对业务扩展性的思考。

第三,补充一个数据可视化页面。例如统计每月销售额、销售趋势、药品销售排行榜,用ECharts画柱状图和饼图。这种页面视觉效果好,又能在答辩演示时讲出“我用ECharts做了数据可视化分析”,是很划算的加分项。

第四,尝试完善系统日志模块。每次登录、新增、修改、删除操作都记录到一张日志表里,前端提供一个日志查询页面。其实很多系统都会有日志需求,能做出来会显得工程意识很到位。

我个人在实际操作中的体验是,这类毕设项目一定要亲自把整个流程跑通至少两遍:第一遍按文档走,第二遍尝试不看文档重新部署一次。在这个过程中会暴露大量问题,比如SQL脚本的编码、依赖版本冲突、前端跨域配置。等这些问题全部处理完之后,你对项目的理解、对SpringBoot和Vue这套技术栈的掌握,会有一个明显的提升。答辩时不管老师问什么,你都能基于自己实际踩坑的经验给出有底气的回答。

最后再分享一个小技巧:拿到一个项目源码后,第一件事不要急着启动,先花半小时把所有代码文件过一遍,看清目录结构、数据表、接口路径。这个习惯能帮你节省至少两小时的调试时间,也会让你对项目的掌控力完全不同。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询