资讯动态

SpringBoot+Vue宠物爱心组织管理系统:从数据库设计到部署实战

发布时间:2026/10/7 3:19:39 来源:尧图企业网站定制
我们站最近在帮一个流浪动物救助站做系统他们之前的台账就是一本A4纸登记本加几个微信群宠物信息、领养申请、捐赠记录全都散在各处经常出现“这只猫到底有没有被领走”都说不清的情况。我当时就想这个场景非常适合做一个后台管理系统把宠物档案、领养申请、捐赠财务、志愿者活动全部收拢到一套流程里。于是就做了这个基于SpringBootVue的宠物爱心组织管理系统。这套系统的技术栈很经典后端是SpringBoot MyBatis MySQL前端是Vue Element UI前后端分离开发RESTful接口对接。它能覆盖一个宠物爱心组织最核心的日常管理需求宠物信息登记与维护、领养申请审核流程、捐赠记录管理、志愿者活动发布与报名以及后台管理员对全局数据的统计与导出的需求。项目的完整源码我已经整理好了Java MySQL MyBatis前端Vue完整工程拿回去改改就能跑。如果你是正在准备毕业设计的学生或者刚开始学全栈开发想找一个练手项目再或者是公益组织想做内部信息化的技术志愿者这套系统的设计和代码都可以直接参考。这篇博文我会把整个项目的思路拆开讲清楚重点是数据库怎么设计、领养流程的状态机怎么实现、前后端权限怎么控制、打包部署有哪些坑以及我实测过程中踩过的几个有代表性的问题。文末我会放一个常见问题速查表方便你以后照表排查。1. 整体方案设计与技术选型这个系统解决什么问题1.1 需求拆解动物救助站的本质需求不是CRUD很多人看到“管理系统”四个字第一反应就是增删改查。其实宠物爱心组织的管理复杂度一点都不低它不是简单把数据存起来就完了。我最初和救助站义工聊天的时候列了一长串问题一只流浪猫从救助到被领养中间要经历驱虫、疫苗、绝育、临时寄养、领养人审核、回访这些信息分散在不同人手里谁能随时查到这只猫的最新状态领养人提交申请之后审核到哪一步了有没有人跟进一次性看全流程很难。捐赠收入和支出都是转账截图加微信群消息月底对账基本靠记忆。志愿者活动发布之后报名人员统计靠接龙做完了也没有沉淀。所以这个系统在设计阶段就不能只做“宠物表增删改查”而是要有清晰的角色地图和业务闭环。最终我划分出了三类使用者角色核心使用场景关注的数据普通访客/领养人浏览宠物列表、查看宠物详情、提交领养申请、在线捐赠登记宠物基础信息、领养进度志愿者登记流浪动物发现信息、参与寄养记录、查看活动安排待救助记录、个人参与记录系统管理员审核领养申请、维护宠物档案、管理捐赠与支出、发布活动、管理用户全局统计、待审核列表基于这个角色划分系统的模块就清晰了宠物管理、领养申请管理、捐赠管理、志愿者活动管理、用户权限管理、首页数据看板。每一块都是围绕真实业务场景去设计的不是凭空造功能。1.2 为什么选SpringBoot Vue MyBatis这套组合技术选型这个问题说实话是很多做毕设或练手项目的朋友最纠结的。我的建议很直接如果你不是为了进某个特定公司而硬着头皮学冷门框架那就选生态最成熟的组合也就是SpringBoot MySQL MyBatis/MyBatis-Plus前端配Vue。理由有几点。第一SpringBoot把传统SSH时代那些繁琐的XML配置全部内置掉了。你创建一个Spring Initializr项目勾选Web、MyBatis、MySQL Driver生成出来就能直接写接口。内嵌Tomcat意味着本地开发不用单独装Tomcat打包成jar就能跑。这对交付给公益组织使用尤其方便对方不需要会配置服务器双击jar就启动。第二MyBatis的优势在于SQL完全由你掌控。宠物爱心组织这类管理系统查询条件非常灵活按品种筛、按年龄段筛、按是否绝育筛、按所在区域筛、按领养状态筛还可能组合条件。MyBatis的动态SQL在这种场景下写起来特别顺手——if标签拼条件比JPA那种自动生成的finder方法直观得多。另一层考虑是MyBatis上手门槛低新手只要会写SQL就会用MyBatis。第三前端选Vue是因为它跟后端接口对接非常自然。Vue的响应式数据和组件化开发在实现宠物卡片、领养表单、后台表格这些界面时有天然优势。你写一个宠物卡片组件传入pet对象页面自动更新写一个领养申请表单用v-model绑定表单字段提交时校验规则也成熟稳定。生态上Element UI组件库几乎就是管理系统的标配能省下大量样式时间。这里要特别说明一个选型细节我用的是Vue 2 Element UI而不是最新的Vue 3 Element Plus。不是说新技术不好而是考虑到两个现实问题。一是Element UI对Vue 2的支持最稳定踩坑资料也最多遇到问题搜索一下基本都有答案二是这个项目要兼顾部署在服务器上的Nginx兼容性Vue 2生态经过大量生产环境验证。如果你是自己在本地学习用Vue 3 Vite Element Plus也完全可以思路一致就是依赖版本需要同步换掉。1.3 项目结构前后端分离的目录规划前后端分离的项目一开始最好就把目录结构想清楚不然后面代码会越写越乱。我最终定下来的结构是pet-adoption-system ├── backend # SpringBoot后端工程 │ ├── src/main/java/com/pet │ │ ├── controller # 接口层 │ │ ├── service # 业务逻辑层 │ │ │ └── impl │ │ ├── mapper # MyBatis的Mapper接口 │ │ ├── entity # 数据库实体类 │ │ ├── dto # 前端交互用数据传输对象 │ │ ├── config # 配置类跨域、静态资源映射等 │ │ ├── common # 统一返回体、异常处理、工具类 │ │ └── interceptor # 登录拦截器 │ └── src/main/resources │ ├── mapper # MyBatis的XML映射文件 │ └── application.yml # 配置文件 ├── frontend # Vue前端工程 │ ├── src │ │ ├── api # axios请求封装 │ │ ├── router # 路由配置 │ │ ├── store # Vuex状态管理 │ │ ├── views # 页面组件 │ │ │ ├── admin # 后台管理页面 │ │ │ ├── adoption # 领养相关页面 │ │ │ └── home # 前台浏览页面 │ │ └── components # 公共组件 │ └── public这个结构最大的好处是分工明确Controller只负责参数接收和结果返回Service处理业务逻辑Mapper只写SQL。前端每个页面一个目录公共组件放components里API统一封装在api目录下。后面不管加功能还是排查问题都能快速定位。2. 数据库设计与核心功能模块2.1 核心表结构一张图看懂数据关系数据库是管理系统的地基这一块我花的时间最多。我设计表的时候有一个原则宁可字段多一点也别漏掉业务需要的信息。宠物爱心组织管理系统涉及的实体比较多我列出最核心的几张表。表名用途关键字段user用户表管理员/志愿者/领养人username, password, role, phone, real_namepet宠物信息表name, type, breed, gender, age, health_status, sterilized, vaccinated, location, status, cover_imageadoption_application领养申请表user_id, pet_id, reason, experience, status, apply_time, review_timedonation_record捐赠记录表user_id, amount, donation_type, remark, create_timevolunteer_activity志愿活动表title, content, location, start_time, end_time, max_participants, statusactivity_enrollment活动报名表activity_id, user_id, enroll_timenotice公告表title, content, publish_time, publisher_id每张表都加了create_time和update_time这两个通用时间字段这是习惯问题排查数据问题的时候太有用了。另外所有表都采用逻辑删除deleted字段0正常1删除而不是物理删除。公益组织的数据很宝贵万一误删了宠物档案恢复成本很高逻辑删除可以在界面上“删掉”但数据库里还留着痕迹。宠物表里的status字段值得单独说明它用的不是简单的字符串而是固定值枚举。我用的是TINYINT类型存储0表示待领养1表示申请中2表示已领养3表示暂不开放领养。为什么要用数字不用“waiting”“adopting”这种英文单词因为数字在代码里维护枚举更方便传输消耗也小。但这里有个注意点必须在前端和后端同时维护好状态值的对应关系否则会出现“数据库的值是2前端显示了一堆乱码”的情况。我是在后端写了一个枚举类前端用了一个状态映射JavaScript文件两边对齐就不会出错。捐赠记录表里我特别加了一个donation_type字段区分现金捐赠和物资捐赠猫粮、猫砂、药品等。这在真实场景中非常必要因为救助站收到的很多是实物不能只记金额。另外我建议加一个remark字段用于记录捐赠备注例如“指定用于猫咪绝育”“定向给救助站的小橘猫”。这个设计看似不起眼但在公益组织对账的时候非常实用。2.2 领养流程的状态机最核心的业务逻辑领养审核是整个系统里最复杂的业务环节。一个完整的领养流程应该是用户提交申请 → 管理员初审 → 志愿者线下联系/面谈 → 通过/拒绝 → 宠物状态变为已领养。如果不用状态机来管理很容易出现“用户申请了管理员却不知道怎么推进宠物被领走了申请状态还没变”的问题。我在adoption_application表里设计了status字段取值规则如下状态值含义可流转到的状态0待审核用户刚提交1初审通过3已拒绝1初审通过2完成领养3已拒绝2已领养终态无3已拒绝终态无这个状态机在Service层实现核心代码逻辑是只有当前状态等于预期值时才允许更新否则抛异常。比如管理员要把申请从0改成1那么SQL的update语句必须带上WHERE status 0如果更新影响行数为0说明当前状态已经不是待审核了可能是别人已经处理了这条申请。这个做法叫乐观锁虽然系统里没有真正的并发冲突管理员也就一两三个人但它能防止数据错误流转。还有一个关键联动的逻辑申请状态变为2已领养时宠物表里的status字段必须同时由0待领养改为2已领养。这两个操作必须在同一个事务里完成否则可能出现“宠物已经被领养走了但宠物列表还在展示它待领养”的脏数据。这一点我在代码里是加了Transactional注解来解决的这也是面试时一个很好的考察点。2.3 首页统计看板让数据驱动决策公益组织也需要数据看板这不是花架子而是实际管理工具。救助站负责人最关心几个问题现在有多少只宠物在待领养这个月的领养率怎么样捐赠收款是不是稳定我做了首页统计接口支持以下几个核心指标宠物总数按类型猫/狗/其他分组统计数量。待领养数量直接查pet表中status为0的记录数。本月新增领养数adoption_application中status为2且create_time在当月1号之后的记录数。本月捐赠总金额donation_record表中donation_type为现金且金额汇总。待审核申请数adoption_application中status为0的记录数。这些统计SQL写起来不难关键是索引要加对。adoption_application表我建了联合索引(status, create_time)pet表建了(status, type)的索引这样统计SQL的查询效率会好很多。数据量小的时候感觉不明显但要是跑了两三年数据积累多了没有索引的统计SQL会明显变慢。首页看板还能延伸出一个很有价值的模块领养趋势曲线。按月份统计过去12个月的领养数量可以看到春秋季是不是领养高峰或者哪个时段活动效果好。我之前给救助站做的版本里把这个曲线图放在首页他们负责人说最大的作用是申请活动经费时有数据依据了。3. 后端核心实现与踩坑记录3.1 统一返回体与全局异常处理前后端分离之后前端和后端必须约定好接口返回格式。我在项目里定义了一个统一的Result类结构很简单public class ResultT { private Integer code; // 200成功500失败 private String msg; // 提示信息 private T data; // 返回数据 // 对应getter/setter以及success()、error()、success(T data)等静态方法 }所有Controller的返回值都是Result类型前端拿到之后统一判断code。如果code为200则取data否则弹出msg提示。这是一个非常通用的做法最大好处是前端不用处理各种“有时候返回对象有时候返回字符串”的脏数据情况。配合统一返回体的是全局异常处理。我写了一个RestControllerAdvice类用ExceptionHandler捕获异常。业务异常比如“该宠物已被领养无法提交申请”我会主动抛出BusinessException全局异常处理器会把它转成Result.error(msg)其他未知异常则统一记录日志并返回“系统异常请稍后重试”。这样做的目的在于用户看到的错误永远是可读的中文提示而不是一串让人看不懂的堆栈信息。这个设计我建议所有SpringBoot项目都加上成本很低但收益很大。曾经有个版本我没做统一异常处理前端经常收到500外加英文错误信息排查一个参数问题来回翻日志效率极低。后来加了全局异常处理接口直接返回中文提示自测阶段省了很多时间。3.2 登录认证与访问权限控制权限控制是管理系统的重点这个项目涉及三类角色肯定不能让普通用户直接调管理接口。我的方案是JWTJSON Web Token加SpringBoot拦截器没有引入Spring Security因为这套系统的角色模型不算复杂用Security反而增加学习成本但思路是标准的前后端分离token认证模式。用户登录成功之后后端生成一个JWT token返回给前端。token里我存了三个信息userId、username、role。前端拿到token后放在axios的每个请求头里格式是Authorization: Bearer xxx。后端加了一个拦截器统一从请求头解析token解析成功就把userId和role放到ThreadLocal里方便后续代码获取当前用户信息解析失败直接返回401状态码。这里有一个拦截器设计的细节有些接口是要放行的比如首页宠物列表、宠物详情游客不需要登录就能看。我配置的是/pet/list、/pet/detail/**、/user/login、/user/register这些路径不加拦截其他接口都要登录。管理端接口再用一个小工具类检查角色比如/admin/**路径要求role必须是ADMIN。如果role不对返回“无权限访问”的提示。做这个模块的时候我踩过一个坑token过期时间设太短了。第一次实现我设的是两小时结果救助站义工在手机上用着用着就突然要重新登录体验很不好。后来我改成了7天过期。你要是做一个正式上线的系统token过期时间建议设置7到14天毕竟不是金融类应用安全级别不需要那么高但便利性很重要。3.3 MyBatis使用的关键细节与性能优化MyBatis是这个项目持久层的核心我实际用下来的感受是动态SQL非常好用但配置上有些细节不处理好会白踩很多坑。第一个是驼峰映射。数据库字段名通常是下划线风格比如real_name、apply_time而Java实体类字段是驼峰风格realName、applyTime。如果不在application.yml里开启驼峰映射你会发现查询结果全是null。配置如下mybatis: configuration: map-underscore-to-camel-case: true这个配置几乎是必须的写SQL时不需要用AS realName这种别名手动对齐。但注意开启驼峰映射后如果你在写SQL时用了自定义别名比如SELECT u.real_name AS userRealName反而会被映射搞乱。所以建议要么全程靠自动映射要么全都写别名别混用。第二个是动态SQL的使用。宠物列表查询是我用的最多的功能因为前台界面上有一个筛选栏可以按宠物类型、品种、性别、是否绝育、所在地区、当前状态来组合筛选。如果用Java代码拼SQL字符串不仅容易拼错还有SQL注入风险。我用的是MyBatis的where标签select idselectPetList resultTypecom.pet.entity.Pet SELECT * FROM pet where if testtype ! null and type ! AND type #{type} /if if testgender ! null and gender ! AND gender #{gender} /if if teststatus ! null AND status #{status} /if if testkeyword ! null and keyword ! AND (name LIKE CONCAT(%, #{keyword}, %) OR description LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY create_time DESC /selectwhere标签会自动处理第一个AND不用手动写WHERE 11这种写法。这里要注意模糊查询的写法我用的是CONCAT(%, #{keyword}, %)而不是%${keyword}%后者存在SQL注入风险${}是字符串拼接这种用户输入一定要避免直接拼SQL。第三个是分页。项目里多个列表页都有分页需求。我引入的是MyBatis的分页插件PageHelper用法极其简单Controller里写一行PageHelper.startPage(pageNum, pageSize);紧跟其后的查询自动分页返回时PageInfo包含总条数、总页数这些信息。但要注意PageHelper的一个典型使用禁忌PageHelper.startPage()必须紧跟第一条查询语句中间不能有任何其他SQL执行。我见过有人把startPage放在前面结果后面先执行了一次count查询或别的查询分页就作用到错的SQL上去了查出来数据完全不对。3.4 文件上传宠物图片与证件照片宠物管理必须要有图片功能没有图片救助站很难展示待领养的宠物。我做的方案是本地文件存储没有用FastDFS或者OSS因为公益组织不一定有云服务预算本地存储配合Nginx访问图片是最经济可靠的方式。流程是这样的前端通过Element UI的Upload组件选择图片axios以multipart/form-data格式POST到后端的/file/upload接口。后端接收MultipartFile校验文件大小限制2MB以内和扩展名只允许jpg、png、gif然后生成一个新的文件名UUID 原始扩展名。存储路径是配置在application.yml里的绝对路径比如/home/pet/images/。生成完毕之后接口返回给前端的是一个可访问的URL路径比如/images/20250215/uuid.jpg。为什么要用UUID重命名而不是保留原文件名原因有两个一是避免中文文件名在URL解析时出现乱码二是避免不同用户上传同名文件互相覆盖。这个项目里我遇到过真实的坑两个志愿者上传了同一张命名为“猫咪.jpg”的照片如果没有重命名第二个人就会把第一个人的照片覆盖掉。为了能让Nginx直接访问图片我做了两件事。开发环境是在后端加了一个静态资源映射配置把本地存储路径映射成/images/**的虚拟路径生产环境则是Nginx配置了一个location指向存储目录这样图片链接就能在浏览器里正常访问。这个思路对中小型项目非常够用。4. 前端Vue实现与交互细节4.1 路由规划与登录状态管理前端工程我用的是Vue Router的history模式页面结构分两个大类前台展示页面和管理后台页面。前台页面包括宠物列表、宠物详情、领养申请、活动列表、公告详情这些对游客开放。管理后台页面包括宠物管理、申请审核、捐赠记录、活动管理、用户管理这些页面都在一个AdminLayout布局下面左侧是菜单右侧是内容区域。路由守卫是整个前端权限控制的关键环节。我在main.js里配置了全局前置守卫每次路由跳转时读取Vuex里存好的token和用户信息router.beforeEach((to, from, next) { const token store.state.token; if (to.path.startsWith(/admin)) { if (!token) { next(/login); } else if (store.state.userRole ! ADMIN) { next(/); } else { next(); } } else { next(); } });这是一个简单但非常可靠的拦截逻辑。需要注意虽然前端做了路由守卫但真正的权限控制必须在后端接口上做。换句话说知道前端拦截不了请求直接调后端接口还是能访问数据的所以前端路由守卫只是提升用户体验后端拦截器才是安全底线。我每次都会跟用这个模板的人强调前端守卫是体验层后端权限是安全层。axios请求拦截器也是必须封装的一环。我在api目录下的request.js里用axios.create生成了一个实例请求拦截器统一从store里拿token塞到请求头响应拦截器统一处理返回结果service.interceptors.response.use( (response) { const res response.data; if (res.code ! 200) { this.$message.error(res.msg); return Promise.reject(new Error(res.msg)); } return res; }, (error) { if (error.response.status 401) { store.commit(logout); router.push(/login); } return Promise.reject(error); } );统一处理token失效和业务错误的体验很重要不然每个页面都要写一遍try/catch和错误提示代码量会倍增且容易漏。4.2 宠物卡片列表与领养申请表单宠物列表页面是整个系统里最“面向公众”的页面我特意做成了卡片流式布局一行三张卡片每张卡片展示宠物图片、名称、品种、性别、绝育标签、当前状态底部有“查看详情”按钮。这个页面用了一个PET_CARD组件接收pet对象作为prop。卡片列表的需求点不在视觉效果而在筛选联动。页面顶部是筛选栏类型下拉框、性别下拉框、状态单选框还有一个关键词搜索框。每次筛选条件变化就重新调用宠物列表API。我是用computed属性把筛选条件拼成一个对象然后用watch监听变化一变化就重新拉数据。这种方式比“点一下查询按钮再刷新”更顺畅用户一选就看结果体验自然。领养申请表单是另一个重点。用户在宠物详情页点击“申请领养”按钮弹出一个表单需要填写真实姓名、联系电话、居住地址、养宠经验、申请理由。这个表单里有几个必填校验我用的是Element UI的rules配置姓名必填长度2-10个字符电话必填正则校验11位手机号地址必填长度10-50个字符养宠经验必填因为这是审核时的重要参考申请理由必填最少10个字字段校验的作用不仅是提交流程顺畅还能减轻管理员审核时的信息判断成本。真实情况下电话写错、地址只写“北京”的申请会浪费管理员很多时间。所以我在前端就要求填写完整后端接口拿到参数后也做一遍同样的校验前端校验可以被绕过后端必须兜底。提交领养申请的接口业务上有一个隐性的判断同一只宠物不能同时出现多条待审核状态的申请记录。所以后端在Service层要检查一下如果这只宠物已经有一条status为0的申请就拒绝新申请提示“该宠物已有待审核的领养申请”。这个细节不是必须的但不做会导致宠物详情页的申请按钮一直能被提交产生多条冲突数据。我这个版本做了检查你写的时候要注意这个逻辑。4.3 Element UI关键组件的坑与优化Element UI在管理系统里用得很多但我实际用下来有几个组件需要注意。第一个是表格的loading状态。后台的宠物管理列表在切换筛选条件或翻页时会有一个网络延迟。如果表格没有loading遮罩用户会以为“点了没反应”然后重复点击或者刷新页面。所以我给每个列表页加了:loadingloading属性发请求前设成true请求完设成false。这个小细节对体验的提升非常明显。第二个是分页组件的封装。后台每个列表页都需要页码、条数、翻页、跳页、总数展示我封装了一个Pagination组件统一接收total、pageNum、pageSize这些参数切换页码时emit一个change事件父组件重新拉数据。这样每个列表页不用重复写分页逻辑也保证样式一致。第三个坑是图片预览。宠物管理的编辑弹窗里需要展示当前图片以及更换图片后的即时预览。Element UI的el-upload组件自带预览功能但要注意用http-request自定义上传时on-success里要拿到后端返回的图片URL再把它同步到表单字段里。我一开始没处理好顺序图片传上去了表单里存的还是旧地址导致保存后图片没变。还有一个我特别想提醒的Element UI的弹窗关闭后弹窗里的表单数据不会自动清空。如果你没有在close事件里重置表单下一次打开弹窗时还会残留上次的数据。我写了一个resetForm方法在弹窗关闭时调用this.$refs.form.resetFields()这个细节能在频繁编辑数据的场景中省下很多麻烦。5. 联调、部署与常见问题排查5.1 跨域问题开发环境的正确配置方式前后端分离的项目跨域问题是绕不开的。开发环境下前端跑在Vue的默认端口8080后端跑在8081端口不一致就会触发跨域限制。我的处理办法是在SpringBoot的配置类里加CorsFilter允许所有来源全局跨域配置开发调试时比较省事。生产环境用的是Nginx反向代理不存在跨域问题。如果你不想在后端解决跨域也可以在前端的vue.config.js里配置代理module.exports { devServer: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } };这个方法最大的好处是后端完全不用感知跨域前端请求都走/api前缀由Vue的devServer代理到后端地址。但要注意代理配置只在开发环境生效生产环境本质上还是要靠Nginx做反向代理否则前后端端口不同还是会报跨域错误。5.2 打包部署实战从jar到Nginx系统的部署流程我建议分成三步每一步都很关键。第一步后端打包。在项目根目录执行mvn clean package -DskipTests生成target目录下的jar包。SpringBoot内嵌了Tomcat所以这个jar是独立的把它传到服务器上执行java -jar pet-server.jar就能启动。但有两个细节一是用-DskipTests跳过测试否则打包时会跑单元测试报错就整包失败二是建议在application.yml里使用外部配置文件的方式用--spring.config.additional-location/opt/pet/application.yml指定外部配置这样改配置不用重新打包。第二步前端打包。在frontend目录执行npm run build生成dist目录。把dist目录下的所有文件上传到Nginx的html目录比如/usr/share/nginx/html/pet然后在Nginx配置里加一个locationlocation /pet/ { alias /usr/share/nginx/html/pet/; try_files $uri $uri/ /pet/index.html; }重点是这个try_files配置它可以解决Vue Router history模式下页面刷新404的问题。原因是history模式的路由路径在服务器上并不存在对应的物理文件必须把所有路径请求都交给index.html让前端路由接管。第三步配置后端接口的反向代理。前端页面里的接口请求路径如果是/api就统一在Nginx里代理到后端服务location /api/ { proxy_pass http://127.0.0.1:8081/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }这样用户访问的是Nginx的80端口完全暴露不到后端端口接口地址也简洁很多。5.3 常见问题排查速查表我整理了开发这套系统时实际遇到的代表性问题和排查方法做成速查表方便你对照排查。现象可能原因排查与解决前端请求接口报404后端Controller路径和前端请求路径不一致或者Nginx代理配置缺少location先看浏览器Network里请求的URL再对照Controller的RequestMapping值生产环境检查代理配置数据库查询结果中文乱码MySQL连接字符串缺少编码参数在jdbcUrl末尾加?useUnicodetruecharacterEncodingutf8且数据库和表都使用utf8mb4字符集图片上传后网页显示不出来静态资源映射或Nginx静态目录配置错误开发环境检查后端配置是否把本地路径映射为/images/生产环境检查Nginx的location是否指向了图片存储目录JWT登录后接口还是401token没在请求头带上前端axios拦截器没配置后端拦截器放行路径配置错误检查浏览器请求头是否带Authorization字段检查后端拦截器的excludePathPatterns是否误放行了接口列表数据一直没变化MyBatis二级缓存导致缓存了旧数据或者前端页面没有重新发请求检查Mapper上是否加了cache配置打开Network看请求是否发出分页数据不对返回全部数据PageHelper.startPage之后没有紧跟着查询语句分页被别的SQL抢先把startPage移到目标查询语句紧前面不要和别的SQL混在一起部署后刷新页面404Vue Router history模式没有配合Nginx的try_files给对应location加上try_files $uri $uri/ /index.html;时间字段差了8小时MySQL时区和SpringBoot默认时区不一致在jdbcUrl上加serverTimezoneAsia/ShanghaiJackson序列化时设置时区UTC8最后再补充一个小技巧。我在项目里给宠物表加了一个sort_weight字段用于控制“新救助的宠物优先展示”和“受伤或老年宠物置顶推荐”之间的平衡。列表查询时ORDER BY sort_weight DESC, create_time DESC管理员在后台可以调整推荐权重。这个字段很轻量但很实用救助站可以把比较难领养的老年猫或残疾猫排到前面提升曝光率。我后来觉得其实还可以进一步扩展成“加急领养”的标签功能页面上一眼看出哪些宠物最需要关注。这套系统上线之后救助站那边的反馈是领养审核从原来的两三周缩短到一周以内捐赠对账的时间从一两天缩短到半小时志愿者报名也不用再翻群消息了。坦白说系统本身没有什么高深的技术但它把原本分散的线下流程规范化了这个价值是实实在在的。如果你也想为本地动物救助组织做点什么又正好在学Java全栈完全可以拿这套代码做基础加一个志愿活动的积分体系或者接一个公众号消息推送让系统的价值再往上走一步。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑