尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring Boot+Vue+MySQL药房管理系统:从架构设计到部署实战

Spring Boot+Vue+MySQL药房管理系统:从架构设计到部署实战 简介本资源是一套面向高校计算机专业毕业设计的完整医药管理系统实战项目聚焦药房信息化管理痛点助力学生快速完成Spring Boot后端Vue前端MySQL数据库的全栈开发实践。压缩包共含多个核心文件以Java源码、Vue前端工程、SQL建表脚本、毕业论文含需求分析与系统设计、答辩PPT及演示视频为主涵盖首页、用户管理、药品/保健品分类与信息管理、疫情常识发布、订单处理及系统权限配置等10大功能模块总大小93.19MB。目前已有77人学习下载资源结构清晰论文逻辑严谨视频真实演示登录、增删改查及权限分配全流程配套开发文档详述环境搭建、接口说明与部署步骤特别适合Java Web与前后端分离课程设计或毕设选题参考。1. 项目整体定位与核心业务梳理1.1 从毕业设计到可落地的药房管理系统先说这个题目的定位。基于Spring BootVueMySQL的逍遥大药房管理系统本质上是典型的前后端分离的管理信息系统在计算机专业毕业设计和课程设计中出现频率极高。它解决的是传统药房用纸质台账或者Excel管理药品时容易出现的几个老大难问题药品批次混乱、库存对不上账、近效期药品无人提醒、销售记录查询费劲、人员权限分不清。这类系统我做过不止一个版本说句实在话药房管理系统的核心复杂度不在于技术本身而在于业务状态多——同一款药品有批次号、生产日期、有效期、入库价、零售价、库存量、供应商、货架位置等多个维度再加上采购入库、销售出库、退货、报损、盘点这些操作数据模型设计不好后期写起来会非常痛苦。这个项目适合谁来参考如果你是计算机相关专业的学生打算做类似选题的毕业设计或者刚入行的开发想搞懂一个完整的前后端分离项目如何从零搭建这套系统的拆解思路都值得看一遍。源码、论文、PPT、开发文档、演示视频的完整配套也意味着你可以直接把它当模板去改造成别的管理类系统——换一个业务领域表格换一换接口重写一遍就是另一个新项目。1.2 核心业务场景与系统角色划分药房管理系统的业务场景可以拆成两类内部管理场景和前端销售场景。内部管理包括药品信息维护、供应商管理、采购入库、库存盘点、效期预警销售场景则是收银开单、处方登记、销售记录查询、退换货处理。根据我平时带项目的经验系统角色建议拆成三类管理员拥有全部权限负责员工账号管理、药品基础信息审核、数据统计查看。药师/营业员日常操作的绝对主力负责药品录入、库存修改、销售开单。采购/库管负责供应商对接、采购入库单创建、入库审核、报损登记。角色权限在前后端都要做。前端控制“这个按钮你能否看见”后端控制“这个接口你能否调用”。很多毕业设计只做了前端按钮隐藏接口不设防这其实是安全漏洞正式答辩时如果老师用Postman直接调接口就会被问住。这个项目在后端权限设计上用了拦截器加角色判断后面我会详细展开。2. 技术选型与架构设计决策2.1 为什么是Spring Boot Vue MySQL这个组合这个组合在近几年已经成为Java方向Web开发的事实标配我个人的看法是它既好写又好讲更适合作为教学和毕业设计的载体。Spring Boot的价值在于零配置快速启动。以前用SSM框架写项目光XML配置文件就能写一天Spring Boot把自动配置、内嵌Tomcat、Starter依赖管理这些都封装好了你只需要在pom.xml里引入依赖就能快速搭起一个可运行的Web服务。对于管理系统这类CRUD密集型的业务系统Spring Boot的生态足够成熟社区资料多遇到问题一搜就有答案。Vue的价值在于渐进式的前端开发体验。药房管理系统有大量表单、表格、弹窗、统计页面Vue的双向数据绑定和组件化开发非常契合这类场景。用Vue CLI或者Vite初始化一个工程配合Element UI组件库后台管理页面的开发效率极高。而且Vue对新手友好模板语法简单路由和状态管理有清晰的官方方案学习曲线比Angular平缓很多。MySQL则是最经典的关系型数据库选型。药房系统的数据高度结构化——药品表、供应商表、订单表、用户表彼此之间有关联关系用MySQL外键加索引就能高效支撑。再加上MySQL免费、轻量、资料多部署和排查都省心。2.2 前后端分离架构是如何落地的前后端分离是这个项目的核心架构思路。前端跑在8080端口由Vue的开发服务器托管后端跑在8081端口由Spring Boot内嵌Tomcat托管。两者通过RESTful API通信数据格式统一用JSON。前后端分离的核心优势是职责清晰、独立部署、并行开发。前端同学只关注页面渲染和交互后端同学只关注接口逻辑和数据处理双方通过接口文档协作。对于毕业设计来说分离架构还有一个隐藏价值——论文里“系统架构设计”那一章可以写出彩拓扑图、接口交互图、数据流向图都是现成的素材。不过分离架构也带来一个开发痛点跨域问题。Vue开发服务器默认端口是8080Spring Boot默认端口是8081浏览器同源策略会拦截跨端口请求。解决办法有几种在Spring Boot中写一个CorsConfig配置类允许指定来源的跨域请求。使用Vue CLI的proxy代理将/api开头的请求转发到后端地址。我惯用的方案是Vue侧代理这样生产环境部署时前端Nginx也能用同样的方式反代后端一套思路用到底。开发时在vue.config.js里这么配module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }这样前端代码里所有请求都写/api/xxx开发时走代理生产时走Nginx反代后端接口不用做任何跨域配置省心很多。2.3 项目工程结构规划拿到源码后第一步不是急着运行而是先把项目结构看懂。这个项目的工程结构分为backendSpring Boot和frontendVue两大块这也是目前企业里最标准的组织方式。后端按分层结构组织包名建议这样划com.yaofang ├── controller // 接收前端请求返回JSON ├── service // 业务逻辑层接口实现 ├── mapper // MyBatis的Mapper接口 ├── entity // 数据库表对应的实体类 ├── common // 统一返回结果、异常处理、工具类 └── config // 配置类比如跨域、拦截器注册前端按页面和组件划分src ├── api // axios请求封装一个模块一个文件 ├── router // 路由配置 ├── store // Vuex状态管理 ├── views // 页面级组件 ├── components // 公共组件侧边栏、头部、表格等 └── utils // 工具函数如request.js这种结构不是随便分的每一层都有清晰的职责边界。Controller只负责参数接收和结果返回不写业务逻辑Service负责业务流程编排和事务控制Mapper只做数据库读写。前端api目录统一管理所有接口地址页面组件里不直接写axios请求这样后端接口路径变了只需要改api目录里的一个文件成本极低。我在做这个项目时吃过亏早期把请求直接写死在页面里后来接口路径调整十几个文件跟着改从那以后我始终坚持“接口请求必须集中封装”。3. 数据库模型设计药房业务的基石3.1 核心表结构规划数据库是整个系统的地基表结构设计好坏直接决定后面写代码的顺畅程度。药房系统最少需要这几张核心表用户表sys_user字段类型说明idbigint主键自增usernamevarchar(50)登录名唯一passwordvarchar(255)BCrypt加密后的密码real_namevarchar(50)真实姓名rolevarchar(20)角色admin/药师/库管statustinyint账号状态1启用 / 0禁用create_timedatetime创建时间药品表drug字段类型说明idbigint主键drug_codevarchar(50)药品编码drug_namevarchar(100)药品名称specvarchar(50)规格如 0.25g*24片unitvarchar(20)单位如 盒/瓶manufacturervarchar(100)生产厂家approval_numbervarchar(50)批准文号purchase_pricedecimal(10,2)进货价sale_pricedecimal(10,2)零售价stock_quantityint当前库存min_stockint库存预警下限shelf_statustinyint上下架状态供应商表supplier字段类型说明idbigint主键supplier_namevarchar(100)供应商名称contact_personvarchar(50)联系人phonevarchar(20)联系电话addressvarchar(200)地址statustinyint合作状态采购入库单表purchase_order字段类型说明idbigint主键order_novarchar(30)入库单号supplier_idbigint供应商IDtotal_amountdecimal(10,2)采购总金额statustinyint状态0待审核/1已入库/2已作废create_timedatetime创建时间入库明细表purchase_order_item记录每个入库单里具体进了哪些药品、数量多少、进价多少。销售订单表sale_order和销售明细表sale_order_item也是同样的结构思路。建议把销售主表、销售明细表拆开而不是把所有信息塞进一张表里。原因很简单销售主表记录“这一单是谁买的、什么时候买的、总价多少”销售明细表记录“这个单里有哪几种药、每种药多少钱”。当你想统计“某种药一个月卖了多少”直接查明细表聚合即可当你想看“某天营业额多少”查主表即可。两张表职责不同查询性能和数据清晰度都好很多。我在设计时还加了个小设计药品编码drug_code加了唯一索引。因为药房的药品录入最容易出现重复——同一款药不同批次、不同厂家录入两次导致一张药品表出现两条相似记录库存各算各的统计报表直接飘了。由于药品编码的唯一性约束至少能挡住一部分人工录入重复问题。3.2 库存与效期预警的实现思路库存预警和效期预警是药房系统区别于普通进销存系统的核心功能也是最容易出彩的设计点。库存预警逻辑drug表里增加了min_stock字段这样当stock_quantity min_stock时系统判定该药品库存不足需要在首页展示预警列表。这个查询用一条SQL就能搞定SELECT drug_name, stock_quantity, min_stock FROM drug WHERE stock_quantity min_stock ORDER BY stock_quantity ASC;效期预警更讲究一些。药房药品最怕的是过期所以必须在药品表里维护valid_until字段有效期至某年某月。预警规则一般分三档效期小于30天红色预警立即处理。效期小于90天黄色预警重点留意。效期小于180天蓝色提醒促销或退回供应商。后端定时任务用Spring Boot自带的Scheduled注解即可实现每天凌晨扫描一次把预警结果写入一张预警记录表前端登录后直接拉取展示。实操提示效期预警不要只做查询一定要结合“入库时登记效期”这个动作。很多系统设计时没考虑效期药品入库只填数量和价格结果效期预警根本没法做。所以在采购入库功能里每一行明细必须包含生产日期和有效期这是药房管理系统的一个关键业务细节。3.3 MyBatis还是JPA数据访问层的选择这个项目的数据访问层用的是MyBatis这也是国内企业的主流选择。MyBatis的优势在于SQL由开发者自己掌控复杂多表查询、动态条件拼接都很灵活。药房系统里“模糊搜索药品名称筛选分类按库存排序”这类动态查询用MyBatis的动态SQL写在XML里结构清晰可读性强。一个典型的多条件查询示例select idselectDrugList resultTypeDrug SELECT * FROM drug where if testdrugName ! null and drugName ! AND drug_name LIKE CONCAT(%, #{drugName}, %) /if if testminStock ! null AND stock_quantity lt; #{minStock} /if /where ORDER BY create_time DESC /selectwhere标签会自动处理第一条件前面的AND问题这个细节新手经常踩坑手写SQL时容易多出一个WHERE AND导致报错。4. 后端核心实现从登录鉴权到业务接口4.1 登录认证与权限控制的完整方案登录认证是管理系统的第一道关。这个项目采用的是Token 拦截器方案比Session方案更适合前后端分离架构。业务流程用户提交用户名和密码。后端校验账号密码校验通过后生成一个UUID或者JWT的Token。将Token存储到Redis中如果有Redis或者内存Map中简化版并设置过期时间。前端将Token存到localStorage每次请求在请求头里带上token字段。后端拦截器拦截除登录接口外的所有/api/**请求校验Token是否存在且有效。登录接口的核心逻辑PostMapping(/login) public Result login(RequestBody LoginDTO loginDTO, HttpServletResponse response) { // 1. 参数非空校验 if (StringUtils.isBlank(loginDTO.getUsername()) || StringUtils.isBlank(loginDTO.getPassword())) { return Result.error(用户名和密码不能为空); } // 2. 根据用户名查询用户 SysUser user userService.findByUsername(loginDTO.getUsername()); if (user null) { return Result.error(用户不存在); } // 3. 校验密码BCrypt加密 if (!BCrypt.checkpw(loginDTO.getPassword(), user.getPassword())) { return Result.error(密码错误); } // 4. 校验账号状态 if (user.getStatus() 0) { return Result.error(账号已被禁用); } // 5. 生成Token String token UUID.randomUUID().toString().replace(-, ); tokenStore.put(token, user.getId()); // 6. 返回用户信息和Token return Result.success(new LoginVO(token, user)); }密码存储用的是BCrypt加密而不是MD5。MD5是哈希算法且不加盐的话很容易被彩虹表破解BCrypt内置盐值每次加密结果都不一样安全性高得多。Spring Security框架里有BCryptPasswordEncoder如果不引入Spring Security单独引入spring-security-crypto依赖也能用。权限控制的业务规则是admin可以访问所有接口药师可以访问销售和药品查询接口库管可以访问采购和库存接口。后端拦截器里在Token校验通过后继续判断用户角色是否在白名单内Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口和静态资源 if (/api/login.equals(request.getRequestURI())) { return true; } // 从请求头获取Token String token request.getHeader(token); if (token null || token.isEmpty()) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录\}); return false; } Long userId tokenStore.get(token); if (userId null) { response.setStatus(401); response.getWriter().write({\code\:401,\msg\:\登录已过期\}); return false; } // 角色校验 SysUser user userService.findById(userId); String role user.getRole(); String uri request.getRequestURI(); if (uri.startsWith(/api/supplier) !admin.equals(role) !purchase.equals(role)) { response.setStatus(403); response.getWriter().write({\code\:403,\msg\:\无权限访问\}); return false; } return true; }注意拦截器写完后一定要在配置类里通过addInterceptors注册并且排除登录接口和静态资源路径。这个注册步骤很多人会忘一旦忘记写好的拦截器不会生效所有请求都能直接进Controller。4.2 药品入库与库存联动的事务处理药品入库是这个项目里最体现事务控制核心价值的接口。一个入库操作同时涉及写入采购订单主表、写入订单明细表、更新药品库存表、写入预警记录表。任何一个环节失败整批数据就会不一致——订单建了但库存没变或者库存变了但订单没记。Spring Boot里用Transactional注解搞定事务Transactional(rollbackFor Exception.class) public void createPurchaseOrder(PurchaseOrderRequest request) { // 1. 创建入库单主表记录 PurchaseOrder order new PurchaseOrder(); order.setOrderNo(generateOrderNo()); order.setSupplierId(request.getSupplierId()); order.setTotalAmount(request.getTotalAmount()); purchaseOrderMapper.insert(order); // 2. 循环写入明细并更新库存 for (PurchaseOrderItem item : request.getItems()) { item.setOrderId(order.getId()); purchaseOrderItemMapper.insert(item); // 3. 库存更新药品存在则累加不存在则新增 Drug drug drugMapper.findByDrugCode(item.getDrugCode()); if (drug ! null) { drug.setStockQuantity(drug.getStockQuantity() item.getQuantity()); drug.setValidUntil(item.getValidUntil()); drugMapper.updateById(drug); } else { // 新药品直接创建 // ... } } // 4. 写入库存预警记录 checkAndInsertStockWarning(order.getId()); }rollbackFor Exception.class的意思是任何异常都触发回滚。注意一点Transactional只对通过Spring代理调用的方法生效如果这个方法是在同一个类内部被另一个方法调用事务会失效。这是Spring事务的经典坑我在项目里测试时遇到过排查了好一阵子才发现是同类内调用导致代理失效。4.3 销售下单与库存扣减的防超卖设计销售模块的库存扣减如果不做并发控制会出现一个经典问题——超卖。两个用户同时买同一款药各买10盒库存剩15盒不加控制的话两笔订单都能提交成功库存变成-5。解决办法是更新库存时加上库存数量条件UPDATE drug SET stock_quantity stock_quantity - #{quantity} WHERE id #{drugId} AND stock_quantity #{quantity}然后判断受影响行数如果返回0说明库存不足直接抛异常回滚。这种乐观锁思路在单机应用里足够用不需要引入Redisson分布式锁这种重量级方案。药房系统的并发量级远达不到抢购场景这个方案在性能和正确性之间取得了很好的平衡。销售下单的完整事务包括创建销售主单。循环扣减库存判断扣减结果。写入销售明细。如果是处方药关联处方记录。更新当日营收统计缓存。这样一个事务下来任何一步失败整单撤销数据一致性有保障。5. 前端Vue实现页面组织与接口对接5.1 路由设计从登录到业务模块的导航结构前端用Vue Router管理页面跳转路由设计直接决定用户操作体验和代码组织。这个项目的路由设计分两层公共页面登录页、首页和业务模块页面药品管理、采购管理、销售管理、供应商管理、系统管理。路由的写法const routes [ { path: /login, component: Login }, { path: /, component: Layout, redirect: /dashboard, children: [ { path: dashboard, component: Dashboard, meta: { title: 首页, icon: home } }, { path: drug, component: DrugList, meta: { title: 药品管理, icon: drug } }, { path: purchase, component: PurchaseOrder, meta: { title: 采购入库, icon: purchase } }, { path: sale, component: SaleOrder, meta: { title: 销售开单, icon: sale } }, { path: supplier, component: SupplierManage, meta: { title: 供应商管理, icon: supplier } }, { path: user, component: UserManage, meta: { title: 系统管理, icon: user } } ] } ];Layout是一个整体布局组件包含侧边栏菜单、顶部栏、内容区三个部分。子路由通过router-view在Layout的内容区渲染。这种设计的好处是框架代码只写一遍新增业务页面只需要在children里加一条路由即可。导航守卫是前端权限控制的重要环节。未登录用户不允许访问业务页面router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { next(); } else { if (token) { next(); } else { next(/login); } } });这个守卫虽然简单但非常实用。很多管理系统没有做这个用户直接输入网址就能跳到业务页面然后因为后端接口返回401才被弹回体验很差。前端先做一层拦截用户没有登录态访问任何业务页面都会直接跳转到登录页。5.2 核心功能页面的交互实现登录页的交互是老生常谈但必须做好的部分表单校验、加载状态、错误提示。用Element UI的Form组件实现校验用户名必填、密码必填且长度不少于6位提交按钮在请求期间禁用防止重复提交。药品管理页面是整个系统的核心页面它包含了列表展示、条件搜索、新增、编辑、删除、上下架切换、分页这些常见交互。关键代码框架template div classdrug-manage !-- 搜索栏 -- el-form :inlinetrue el-form-item label药品名称 el-input v-modelqueryParams.drugName placeholder请输入药品名称 / /el-form-item el-form-item el-button typeprimary clickhandleSearch查询/el-button el-button clickhandleReset重置/el-button /el-form-item /el-form !-- 药品列表 -- el-table :datadrugList v-loadingloading el-table-column propdrugCode label药品编码 width120 / el-table-column propdrugName label药品名称 min-width150 / el-table-column propspec label规格 width120 / el-table-column propstockQuantity label库存 width80 / el-table-column propsalePrice label零售价 width100 / el-table-column label操作 width200 template #default{ row } el-button typeprimary link clickhandleEdit(row)编辑/el-button el-button typedanger link clickhandleDelete(row)删除/el-button /template /el-table-column /el-table !-- 分页 -- el-pagination v-model:current-pagequeryParams.current :page-sizequeryParams.size :totaltotal layouttotal, prev, pager, next current-changehandlePageChange / /div /template销售开单页面的交互要稍微复杂一点它是典型的“购物车”模式左侧是药品搜索和药品列表点击药品加入右侧购物车购物车内修改数量、计算总价确认结账后生成销售单。这个小设计非常贴合药房收银场景也是答辩时的加分项。接口请求统一封装在request.js里用axios的拦截器统一处理Token注入和错误提示import axios from axios; import { ElMessage } from element-plus; import router from /router; const request axios.create({ baseURL: /api, timeout: 10000 }); // 请求拦截器注入Token request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.token token; } return config; }); // 响应拦截器统一处理错误 request.interceptors.response.use( response { const res response.data; if (res.code ! 200) { ElMessage.error(res.msg || 请求失败); return Promise.reject(new Error(res.msg)); } return res; }, error { if (error.response error.response.status 401) { ElMessage.error(登录已过期请重新登录); localStorage.removeItem(token); router.push(/login); } else { ElMessage.error(网络异常请稍后重试); } return Promise.reject(error); } );这个拦截器就是前后端联调的关键。没有它每个页面都要自己处理Token和401代码重复不说还容易出现漏处理的情况。集中管理后后端不管返回什么错误前端都能统一提示用户体验一致性也有保障。5.3 Vuex状态管理与前端数据流Vuex在这个项目里承载两类状态一是当前登录用户信息二是系统级数据如药品分类字典、库存预警总数。用户信息在登录成功后写入Vuex同时存一份到localStorage防止页面刷新丢失。const store createStore({ state: { userInfo: JSON.parse(localStorage.getItem(userInfo) || {}), token: localStorage.getItem(token) || }, mutations: { setUser(state, user) { state.userInfo user; localStorage.setItem(userInfo, JSON.stringify(user)); }, setToken(state, token) { state.token token; localStorage.setItem(token, token); }, logout(state) { state.userInfo {}; state.token ; localStorage.removeItem(userInfo); localStorage.removeItem(token); } }, actions: { async login({ commit }, form) { const res await loginApi(form); commit(setToken, res.data.token); commit(setUser, res.data.user); return res; } } });如果页面刷新Vue的响应式数据会清空但localStorage里的数据还在所以state初始化时直接读localStorage这样用户刷新页面后登录状态依然保留不用重新登录。6. 部署上线与常见问题排查实录6.1 从零到一本地环境的完整搭建步骤拿到项目源码后按照下面的步骤能跑起来安装MySQL推荐用MySQL 5.7或8.0版本。安装完成后创建数据库CREATE DATABASE yaofang DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;导入数据库脚本源码的sql目录下通常有yaofang.sql用命令行或者Navicat导入即可。修改后端配置在application.yml中配置数据库用户名密码spring: datasource: url: jdbc:mysql://localhost:3306/yaofang?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver注意MySQL 8.0的驱动类名是com.mysql.cj.jdbc.DriverMySQL 5.7可以用com.mysql.jdbc.Driver版本不匹配会报ClassNotFoundException。启动后端用IDEA打开backend目录等待Maven下载依赖运行启动类。启动前端cd frontend npm install npm run serve访问系统浏览器打开http://localhost:8080使用初始账号admin/admin123登录。环境搭建过程中最常见的坑是npm install太慢或者失败这种时候用国内镜像源npm config set registry https://registry.npmmirror.comMaven依赖下载慢的话在settings.xml中配置阿里云镜像仓库。6.2 运行过程中的高频问题与解决技巧我把项目中遇到的高频问题整理成了一张速查表方便你对照排查问题现象可能原因解决方案前端页面能打开但接口返回404请求路径写错或后端接口未启动检查后端控制台是否启动成功核对API路径和前端api目录定义是否一致登录接口返回401用户名密码错误或账号被禁用检查数据库sys_user表的账号状态密码是否正确药品列表接口返回500数据库表字段与实体类不匹配检查MyBatis映射文件比对实体类字段和数据库表字段前端请求一直Pending跨域问题或后端端口被占用检查代理配置确认后端启动端口与代理目标一致npm install报错依赖版本冲突或Node版本过低看错误日志必要时删除node_modules重新安装启动时端口被占用8080或8081已被其他程序占用修改application.yml中的server.port或修改vue.config.js中的devServer.port药品入库后库存没有变化事务没有提交或更新库存逻辑有误检查Transactional注解是否生效看数据库日志的SQL执行情况还有一个开发必踩的坑数据库连接时区问题。MySQL连接URL忘记加serverTimezoneAsia/Shanghai启动时会报时区错误。这个配置一定要加上否则在不同版本的MySQL和JDBC驱动组合下可能出现数据时间偏差8小时的问题。6.3 从开发到部署完整上线的经验如果这个系统要真正部署上线而不仅仅是本地跑通我建议按下面的思路来做后端打成jar包放到服务器上运行mvn clean package -DskipTests java -jar target/yaofang-0.0.1-SNAPSHOT.jar前端打包成静态文件用Nginx托管npm run build # 生成的dist目录就是静态文件Nginx还需要配置反向代理把/api请求转发到后端服务server { listen 80; server_name your-domain.com; location / { root /var/www/yaofang/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里try_files $uri $uri/ /index.html;很关键Vue Router的history模式刷新页面时会请求服务器上的真实路径Nginx找不到对应文件就返回404加上这行配置后所有路径都回退到首页再由Vue Router去匹配路由。如果不加这句刷新按钮一按页面就白屏这是前后端分离项目部署最高的一个坑。数据库在生产环境建议用MySQL 8.0字符集选utf8mb4——它能存emoji和一些特殊符号utf8在遇到生僻字时可能会报错。另外给数据库做个定时备份用crontab每天凌晨执行0 2 * * * mysqldump -u root -p123456 yaofang /backup/yaofang_$(date \%Y\%m\%d).sql7. 个人实操中的几点体会这个项目我完整跑过不止一遍有一件事印象最深数据库设计比写代码更重要。如果拿到题目的第一件事是急着启动项目写代码后面八成会陷入表结构反复改、接口反复调整的泥潭。先花半天时间把业务流程图和数据表结构梳理清楚后面写代码的效率反而是最高的。药房系统里药品、供应商、采购单、销售单这些实体关系理清楚其实你已经完成了一半工作量。还有一点想特别提醒看源码时不要只看Controller和Mapper要把整个请求链路串起来看。拿到一个功能后先看前端页面怎么写再看api目录里请求路径接着看Controller接收什么参数、Service处理什么业务、Mapper执行什么SQL。整个链路走一遍你才算真正看懂了这段代码。开发文档和演示视频这个配套值得好好利用。演示视频能看到系统运行效果开发文档里有环境搭建步骤论文里有需求分析和系统设计思路。先看视频了解系统面貌再读文档掌握结构最后跟论文对着看代码这是最高效的学习顺序比拿起来就盲目运行源码强得多。最后如果你打算在这个项目基础上扩展我建议优先考虑这几个方向加入药品批号和效期的独立管理、用ECharts做销售数据可视化图表、引入Redis缓存热点数据和Token、把密码登录升级为验证码登录。任何一个方向做出来都能让这个项目的完成度和答辩亮点上一个台阶。本文还有配套的精品资源点击获取
返回列表