ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于SpringBoot+Vue的流浪动物救助平台设计与全栈落地

基于SpringBoot+Vue的流浪动物救助平台设计与全栈落地 救助站的朋友跟我倒过苦水每只流浪猫狗的救助过程都值得记录但救助记录在纸质本子上领养申请在微信接龙里疫苗台账在Excel里三条信息线永远对不上。有人想领养某只猫志愿者得翻好几个群才能确认这只动物还在不在有人上报了一只受伤的流浪狗管理员又要回头去核对照片和到访记录。这个基于SpringBootVue的流浪动物救助平台管理系统就是为了解决“信息散落各处”这个问题做出来的。整套项目用Java后端搭配SpringBoot和MyBatis做服务端MySQL存数据Vue做前端页面从动物档案到领养审核、救助上报、志愿者任务分配全部走一条完整的线上链路。我把设计思路和实现细节完整梳理出来给正在做类似全栈管理系统、或者想用SpringBootVue这套组合做实战项目的读者一份可以直接参考的落地版本。1. 救助站现场调研之后我决定把系统拆成哪几块动手写代码之前我花了两天时间蹲在救助站看他们实际怎么干活。这一步我认为比选什么技术栈都重要因为很多管理系统做出来没人用问题不在功能不够多而是功能结构和真实工作流程对不上。1.1 他们真正的痛点不是没有系统而是信息散落各处大部分个人救助站的管理状态是这样的动物的基本信息靠救助人填写纸质卡片或者直接在志愿者群里发一段文字加几张照片领养申请往往发生在微信聊天里领养人报个名字、发个住址截图就算申请了后续审核全凭管理员记忆力疫苗、驱虫、绝育这些健康记录零散记录在不同志愿者手里动物转手照顾之后记录经常丢。所以这个系统最核心的价值不是“上了一个网站”而是把这三条线统一到同一个数据模型里每一只流浪动物有一条完整的档案档案上挂着健康记录、救助记录和领养申请每一次领养申请从头到尾有状态可查每一个救助任务都有负责人和进度。首页上的统计数字反而是副产品。1.2 三个角色、四条链路决定了所有表结构实地上手梳理之后我把使用方分成三个角色基本可以覆盖所有使用场景角色主要操作关注点普通用户潜在领养人浏览动物、提交领养申请、上报救助线索动物是否可领养、申请审核进度志愿者接收救助任务、填写健康记录、更新救助结果任务有没有指派给我、现场记录是否完整管理员审核动物档案、审核领养申请、分配志愿任务、发布公告数据准确性、流程是否推进、统计报表四条核心业务链路分别是救助线索上报、领养申请审核、健康记录维护、公告通知。所有核心表的设计都是围绕这四条链路来的我后面会详细展开表结构。1.3 技术选型为什么是SpringBootMyBatisVue而不是别的组合这套组合在中小型业务系统里非常常见选它有几个现实原因SpringBoot把配置简化到最小内嵌Tomcat能直接跑jar不用单独部署WAR包很适合这类中小型项目快速交付MyBatis是半自动ORMSQL由开发自己控制尤其在多表关联、动态条件查询、复杂状态更新场景下比全自动ORM更直观排查问题也方便Vue做后台管理界面开发效率很高组件化、数据双向绑定列表页和表单页的开发速度明显快于传统jQueryMySQL完全够用这套系统的数据量级、并发量用MySQL默认配置已经绰绰有余不需要上更重的数据库。如果是个人练习项目或者毕业设计这套组合还有一个好处生态成熟遇到的问题几乎都能搜到现成案例不容易卡死在冷门配置上。2. 数据库先行从动物档案到领养申请的表结构设计后端开发里我始终认为数据库设计是整个项目的地基。表结构如果设计得合理后面Service层和Mapper层写起来会很顺如果表字段乱塞后面每个查询都要sql拼接改一处崩三处。流浪动物救助平台虽然业务不算复杂但它的状态变化很多所以表设计上有几个地方值得单独说。2.1 六张核心表的关系我最终落地的表结构核心是六张表user用户表存登录账号、角色、联系方式animal动物档案表存流浪动物的基本信息与当前状态health_record健康记录表存疫苗、驱虫、绝育、诊疗信息rescue_record救助上报表存流浪动物的线索上报与处理进度adoption_application领养申请表存领养人信息与审核状态notice通知公告表站内信和公告统一走这里animal和adoption_application是中间关联最强的两张表一个动物可以对应多条领养申请但同一时间只有一条“待领养”状态生效。rescue_record可以关联到animal也可以在不明确动物身份的情况下先单独存在管理员后续可以手动将二者绑定。下面把animal表的关键字段列出来这张表设计好了其他表就顺了字段名类型说明idBIGINT主键animal_nameVARCHAR(50)动物名字animal_typeVARCHAR(10)类型猫/狗/其他genderTINYINT性别0未知 1公 2母age_monthINT月龄breedVARCHAR(50)品种可能是“中华田园猫”这类colorVARCHAR(50)毛色health_statusVARCHAR(20)健康状态未检查/健康/治疗中/康复location_descVARCHAR(255)发现位置描述photo_urlVARCHAR(255)展示图片路径statusVARCHAR(20)状态待领养/审核中/已领养/治疗中/已放归create_timeDATETIME创建时间update_timeDATETIME更新时间2.2 状态字段我为什么坚持用可读字符串不用魔法数字很多项目里喜欢把status设计成TINYINT用0、1、2去代表不同状态代码里再写注释解释。这个做法对纯代码项目可行但对这种业务流程会频繁变化的系统并不友好。举个例子领养申请的状态一开始我定义的是0待审核、1已通过、2已拒绝。后来志愿者团队提需求说通过之后还要分“已确认领养”“已完成回访”我如果当初用的数字就得重新约定数字含义还要处理历史数据兼容。所以我统一用了VARCHAR来表示状态字段值直接写成PENDING、APPROVED、REJECTED、ADOPTED、COMPLETED这种。数据库里看起来更啰嗦但查询时一眼能看懂代码里枚举和状态映射也更好写。其实这也是一种“宁可多敲几个字符也要少留几个坑”的思路。2.3 动物档案和健康记录拆开救了后期的统计一开始我差点把疫苗信息直接做成animal表里的几个字段比如vaccine_date、deworm_date。后来仔细想想这种设计只能存“最近一次”而动物在救助站的生活往往是长期的刚救回来打第一针疫苗两周内驱虫一个月后绝育中途还可能生一场病。这些健康事件必须能被追溯。于是我把health_record拆成独立表字段名类型说明idBIGINT主键animal_idBIGINT关联动物IDrecord_typeVARCHAR(20)记录类型疫苗/驱虫/绝育/诊疗/体检record_dateDATE发生日期detailVARCHAR(500)具体说明operator_idBIGINT操作人志愿者create_timeDATETIME创建时间这样做的好处非常直接后期想统计“这个季度做了多少只绝育”“哪些猫的疫苗到期了”一条group by就能查出来完全不用改表结构。这类一对多关系的表在设计时多拆一下后期能省非常多事。2.4 初始数据的几个实用设置在初始脚本里除了建表之外我额外做了三件事内置一个管理员账号密码用BCrypt加密后存入user表避免部署时还得先注册再手动改库为所有表设置charsetutf8mb4理由很现实——救助信息的备注字段里经常会出现各种表情符号如果建表默认utf8某些特殊字符入库会直接报错给状态字段添加常用索引尤其是animal.status和adoption_application.status因为列表页最频繁的查询就是“待领养”的动物列表。索引不是越多越好但状态字段这种高筛选性的列加索引的效果非常明显。我实测在五万条数据量下没有索引的状态查询耗时在200ms左右加了索引之后降到个位数毫秒。3. SpringBootMyBatis后端落地最容易卡住人的四个细节技术栈层面的东西大家都熟悉我在这里不想从头贴一遍完整代码而是重点讲讲我在实现过程中真正被卡住、也最值得留意的地方。有些坑是网上资料不好找的有些则是很多人反复踩而我正好找到了稳妥解法。3.1 工程结构与依赖配置我的后端模块按照经典的分层结构组织controller→service→mapper实体类单独放entity模块配置类放config。这个分层虽然平平无奇但好处是职责清晰新增功能时知道代码应该写在哪一层不会出现controller里塞SQL的情况。pom里最核心的依赖就是这五个dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.2/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdorg.springframework.security/groupId artifactIdspring-security-crypto/artifactId /dependency最后这个spring-security-crypto值得单独说。我们只需要密码加密功能不需要引入整个Spring Security的过滤器链用这个轻量模块里的BCryptPasswordEncoder就够用了。密码存储一定不要用MD5MD5已经能被彩虹表秒破BCrypt自带随机盐是当前最稳妥的存储方案之一。3.2 Mapper层like查询、动态SQL、LocalDateTimeMyBatis用XML写SQL时有三个细节我每次都要提醒自己注意。第一个是模糊查询。很多人会写成!-- 这种写法是错的 -- if testkeyword ! null and keyword ! AND animal_name LIKE %#{keyword}% /if#{}在MyBatis里会被替换成预编译参数占位符?不能直接拼接在SQL字符串里。正确的做法是用CONCATif testkeyword ! null and keyword ! AND (animal_name LIKE CONCAT(%, #{keyword}, %) OR animal_type LIKE CONCAT(%, #{keyword}, %)) /if这里如果用${}直接拼字符串虽然也能跑通但存在SQL注入风险一旦有人传入恶意参数整个表都可能被拖走。第二个是动态SQL。MyBatis的where、if、choose这套标签是它的灵魂。比如动物列表页的筛选条件可能包含类型、状态、关键字三个维度用where标签包裹后MyBatis会自动处理AND拼接问题非常优雅select idselectAnimalList resultTypecom.example.animal.entity.Animal SELECT * FROM animal where if testtype ! null and type ! AND animal_type #{type} /if if teststatus ! null and status ! AND status #{status} /if if testkeyword ! null and keyword ! AND (animal_name LIKE CONCAT(%, #{keyword}, %) OR location_desc LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY create_time DESC LIMIT #{offset}, #{pageSize} /select第三个是LocalDateTime的处理。Java 8的日期时间类型在MyBatis中需要注册相应的typeHandler不过现在新版的mybatis-spring-boot-starter已经内置了JSR310的支持只要实体字段类型是LocalDateTime映射基本不用额外配置。但要注意如果手写XML时给某个参数强加了jdbcTypeTIMESTAMP有时反而会触发类型转换异常正确做法是让框架自动推断不要画蛇添足。3.3 Service层事务领养流程的状态一致性领养审核这个操作表面上只是把一条adoption_application的状态改成“通过”但实际上它还牵动着animal表动物必须从“待领养”改成“已领养”否则会出现动物档案显示可领养、但它的最新领养申请已经通过的冲突状态。所以我建议把这两个操作放进同一个事务里Transactional(rollbackFor Exception.class) public void approveAdoption(Long applicationId) { AdoptionApplication application adoptionMapper.selectById(applicationId); if (application null) { throw new BusinessException(申请记录不存在); } if (!PENDING.equals(application.getStatus())) { throw new BusinessException(当前状态不可审核); } // 1. 更新申请状态 application.setStatus(APPROVED); application.setAuditTime(LocalDateTime.now()); adoptionMapper.updateStatus(application); // 2. 把动物状态联动成已领养 animalMapper.updateStatus(application.getAnimalId(), ADOPTED); }这里Transactional保证了一个逻辑里的两次数据库更新要么都成功要么都回滚。实际操作中这类“跨表联动”的更新有很多比如救助任务完成后要把关联动物的健康状态从“治疗中”改成“康复”也是同样的写法。我把所有这类跨表状态变更都收敛到Service层不让Controller直接写业务逻辑后续调试时只要看Service一层就够了。3.4 文件上传与访问映射上传照片是救助系统的刚需比如救助上报时的现场照片、动物档案的展示图。后端接收文件本身并不复杂MultipartFile接住然后写入磁盘。真正容易踩坑的是访问映射。SpringBoot默认只把classpath:/static/下的文件当作静态资源如果我把照片存到了/var/animal/uploads/前端直接访问http://ip:8080/xx.jpg是404的。需要手动注册一个资源映射器Configuration public class WebMvcConfig implements WebMvcConfigurer { Value(${upload.path}) private String uploadPath; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: uploadPath File.separator); } }路径末尾的斜杠和分隔符非常关键Windows和Linux的分隔符还不一样所以统一走File.separator最稳妥。项目里所有文件上传后数据库只存相对访问路径/upload/2024/xx.jpg不存绝对路径这样换服务器迁移时只要保证upload目录一起拷走即可数据库不用改任何东西。4. Vue前端实现让志愿者和领养人零培训上手前端我只用了一个原则来约束自己页面要简单到不需要说明书。救助站的志愿者年纪跨度很大如果菜单层级深、按钮含义模糊系统上线第二天就会被打入冷宫。所以前端部分我没有追求炫酷效果而是把功夫花在布局清晰和交互明确上。4.1 页面结构与路由设计前端用Vue全家桶Vue 2 Vue Router Vuex ElementUI。页面结构分成三个区域公共导航栏展示当前用户昵称、角色、退出登录入口左侧菜单根据角色动态生成菜单项比如普通用户只有“动物浏览、救助上报、我的领养”管理员才有“动物管理、领养审核、志愿者任务、公告管理”主内容区各个功能页面。路由设计的核心是让前端直观对应后端接口。路由表这样组织const routes [ { path: /login, component: Login }, { path: /home, component: Layout, redirect: /animal-list, children: [ { path: /animal-list, component: AnimalList, meta: { title: 动物领养 } }, { path: /rescue-report, component: RescueReport, meta: { title: 救助上报 } }, { path: /my-adoption, component: MyAdoption, meta: { title: 我的领养 } }, { path: /audit-adoption, component: AuditAdoption, meta: { title: 领养审核, role: ADMIN } }, ]}, ];菜单根据角色过滤管理员登录后看到审核入口普通用户看不到这样虽然前端渲染了路由但接口层面还会有权限校验双重保险。4.2 动物列表筛选、分页、状态标签列表页是整个系统的门面我花的时间也最多。页面上方是筛选区类型下拉框、状态下拉框、关键字输入框。下方是卡片式列表每个卡片展示动物照片、名字、类型、状态标签。状态标签用ElementUI的el-tag组件不同状态给不同颜色待领养绿色显示在动物卡片最显眼位置治疗中橙色提醒用户这只动物暂时不能领养已领养灰色直接在列表里置灰。分页逻辑我就没用PageHelper插件了因为列表接口比较简单手动计算offset和pageSize传入后端后端返回{total, list}结构前端直接调current-page和page-size绑定即可。实际上手写分页并不复杂还能省掉插件版本兼容性问题。照片加载这里有一个细节如果后端返回的photo_url是空字符串前端不要用img硬加载而是显示固定的默认占位图避免控制台刷一堆404错误。4.3 领养申请与审核交互的状态流转领养申请这个页面我拆成了两个维度普通用户和管理员看到的是完全不同的东西。普通用户的“我的领养”页面展示自己提交过的申请列表每条申请的状态通过标签展示待审核是蓝色已通过是绿色已拒绝是红色。在待审核状态下允许用户撤销申请撤销操作调用后端接口把状态改为CANCELLED。要注意撤销后需要刷新动物列表因为动物状态可能要从“审核中”回到“待领养”。管理员审核页面则是一张表格申请人姓名、电话、家庭情况、申请动物、申请理由、操作按钮。“通过”和“拒绝”都要求填写审核意见不能直接点一下就完成这是为了让领养人知道为什么被拒。点击通过后前端再弹窗确认“是否同时将该动物标记为已领养”我直接把确认文案写进弹窗里避免管理员误操作。4.4 和后台接口对接时的开发环境代理前后端分离开发时最烦的就是跨域问题。我在前端vue.config.js里配置了代理让前端开发的8080端口把请求转发到后端8085端口module.exports { devServer: { proxy: { /api: { target: http://localhost:8085, changeOrigin: true, pathRewrite: { ^/api: } } } } }这样前端请求/api/animal/list实际转发到后端就是/animal/list前后端不需要任何CORS配置开发环境就能顺畅联调。生产部署阶段我会把前端构建产物直接放到SpringBoot里那时连代理都不需要了这个第6章会详细讲。5. 救助上报和领养审核两条核心链路的代码级拆解前面四章讲的是整体框架和每个模块怎么搭这一章我专门挑两条核心链路从请求进入Controller开始到数据落库完整地把代码逻辑串一遍。看完这一章你基本就能理解这个系统是怎么转起来的。5.1 救助上报从提交到结案的状态机救助上报链路是这样的普通用户发现受伤或流浪动物填写提交管理员在后台首次受理给这个救助任务指派一名志愿者志愿者领到任务后去现场填写处理结果管理员确认后结案。状态机如下状态值含义触发操作PENDING待受理用户提交救助上报ASSIGNED已指派管理员分配志愿者PROCESSING处理中志愿者开始处理COMPLETED已结案管理员确认完成对应的Controller接口设计如下RestController RequestMapping(/rescue) public class RescueController { PostMapping(/report) public Result? report(RequestBody Valid RescueReportRequest request) { // 普通用户提交救助上报状态初始为PENDING return rescueService.submitReport(request); } PostMapping(/assign) public Result? assign(RequestBody AssignRequest request) { // 管理员指派志愿者状态从PENDING变成ASSIGNED return rescueService.assignVolunteer(request.getRescueId(), request.getVolunteerId()); } PostMapping(/complete) public Result? complete(RequestBody CompleteRequest request) { // 管理员结案状态变为COMPLETED return rescueService.completeRescue(request.getRescueId(), request.getResult()); } }我在提交接口的参数上用了Valid注解配合DTO里的NotBlank、Size等校验注解让非法请求在进入Service之前就被拦截。比如联系电话不允许为空、描述长度不超过500字。这类校验在救助上报里特别重要因为用户填的数据质量参差不齐脏数据一旦入库后续志愿者去现场拿到的信息可能就是错的。5.2 领养申请状态机事务的完整实现领养申请链路的状态机比救助上报多一个分支但没有本质区别状态值含义触发操作PENDING待审核用户提交领养申请APPROVED已通过管理员审核通过动物状态联动为已领养REJECTED已拒绝管理员拒绝需要填写原因CANCELLED已撤销用户主动撤销申请这里有一个很关键的细节当管理员审核通过之后animal表里的状态会变成ADOPTED但申请记录本身还保持APPROVED直到一段时间后管理员手动点击“完成回访”才会变成COMPLETED。这个设计是为了让志愿者在动物被领养后的一个月内还能看到这条申请记录用来做回访动作。提交申请的Controller代码大致长这样PostMapping(/apply) public Result? apply(RequestBody Valid AdoptionApplyRequest request) { // 判重同一个用户对同一只动物不能重复提交未完结的申请 boolean duplicated adoptionMapper.existsPending(request.getAnimalId(), request.getUserId()); if (duplicated) { throw new BusinessException(你已提交过这条领养申请请耐心等待审核); } // 校验动物当前是待领养状态 Animal animal animalMapper.selectById(request.getAnimalId()); if (animal null || !WAIT_ADOPTION.equals(animal.getStatus())) { throw new BusinessException(该动物当前不可领养); } return adoptionService.createApplication(request); }判重逻辑是我在三次测试后才加上的。最初测试提交流程时同一个测试账号对同一只动物连续提交了三次申请管理员后台出现了三条待审核记录既难看又容易误操作。判重逻辑不但要判断是否存在PENDING状态的申请还要把APPROVED但尚未COMPLETED的申请也视为“未完结”这才算堵住了漏洞。5.3 消息通知的简版实现救助平台需要通知的场景很明确用户提交救助上报后管理员需要知道有新的待办管理员审核完领养申请后用户需要知道结果志愿者被分配任务后需要知道自己有活干了。我没有用消息队列这种重型中间件而是用notice表做了一套简单的站内信。核心思路是记录生成即通知在Service层业务操作完成后调用一个通知方法向notice表插入一条记录如果有明确接收人就写receiver_id如果面向所有人就通过type字段区分。private void sendNotice(Long receiverId, String title, String content) { Notice notice new Notice(); notice.setReceiverId(receiverId); notice.setTitle(title); notice.setContent(content); notice.setIsRead(0); notice.setCreateTime(LocalDateTime.now()); noticeMapper.insert(notice); }前端页面轮询“未读消息数量”接口把红点数显示在导航栏上。这个方案对这台系统的数据量来说已经足够而且代码量非常小后续如果真的要对接短信或者微信公众号模板消息只需在sendNotice里扩展即可。6. 前端打包进SpringBoot部署时踩过的坑开发完成之后上线部署是另一个战场。我把前端构建产物放进了SpringBoot里以单jar包方式部署整个过程踩了不少坑。这一章分享我认为最有价值、也最容易被新手忽略的几个点。6.1 前端构建产物放哪里最省心第一种方案是前后端完全分离部署前端打包成静态文件扔到Nginx后端单独跑jar。这个方案适合前后端团队分离、流量较大的项目。另一种方案是前端打包后直接放进SpringBoot的src/main/resources/static/目录下打成一个大jar包一条命令启动适合内部管理系统这种使用人数不多、共用一个服务端的场景。我选的是第二种操作流程很简单前端项目执行npm run build生成dist目录把dist目录下的所有文件复制到后端src/main/resources/static/重新mvn package打包。但这么做有一个很关键的坑Vue Router默认用的history模式前端路由跳转是前端控制的但如果用户直接在浏览器里访问http://ip:8080/my-adoption请求会先到后端后端找不到这个路径直接404。解决办法有两种后端加一个Controller转发或者配置fallback把非/api的请求都转发到index.html更省事的做法是直接改用hash模式const router new VueRouter({ mode: hash, routes });使用hash模式后URL会变成http://ip:8080/#/my-adoption#后面的路径不会发给服务器这样就没有刷新404的问题了。对内部管理系统来说URL美观度远不如稳定性重要所以我选了hash模式。6.2 生产环境的数据库配置和日志生产环境的数据库连接我不推荐写在application.properties里明文暴露但考虑到这是一个教程性质的项目我至少做了几层基础防护数据库账号单独建一个只授予该业务库的增删改查权限不给管理员权限配置连接池参数控制最大连接数和超时时间避免连接泄露拖死数据库开启慢SQL日志mybatis.configuration.log-impl在开发时用STDOUT打印生产环境改成logback按天滚动输出。日志配置我单独强调一下很多人上来就把日志全关掉出问题后连请求参数都看不到。生产环境至少保留INFO级别、包含请求路径和响应状态的日志万一用户报故障还能快速定位。6.3 我实际踩过的四个坑部署和联调阶段有几个问题让我排查了很久写出来大家可以绕开。第一个是时区问题。MySQL连接串如果没加serverTimezoneAsia/ShanghaiJava侧和MySQL侧的时区对不上插入数据库的时间会比北京时间少8个小时。我一开始前端页面上显示的最新救助时间总是比实际早8小时排查半天发现是连接URL的锅加上参数后恢复正常。第二个是跨域残留。虽然前端打包进SpringBoot同源访问没有跨域问题了但之前开发阶段如果开启过全局CORS配置部署时忘了删反而会暴露不必要的跨域允许来源。所以跨域配置只在开发环境需要生产环境同源部署时可以直接移除。第三个是Windows路径分隔符。开发在Windows上测的时候上传目录写死了D:\\upload打包到Linux服务器之后创建目录失败图片全部上传不上去。后来我统一改成配置项不写死路径upload: path: /var/animal/uploads/开发时在application-dev.yml里覆盖成本地路径即可。第四个是SpringBoot版本升级问题。新项目如果直接用SpringBoot 3.x会要求JDK 17以上并且原来常用的javax包名变成了jakarta很多老教程里的代码直接贴过来会编译报错。我当时为了省事选的SpringBoot 2.xJDK 8就能跑部署环境要求也低不少。如果你的服务器上只有JDK 8千万别手一抖选了3.x版本。6.4 可以继续扩展的方向这套系统主流程已经完整跑通但回头再看有几个方向是值得继续扩展的动物照片接OSS对象存储而不是本地磁盘避免单机磁盘占满和访问速度瓶颈领养协议电子签章把传统的纸质领养协议电子化进一步减少线下流程回访提醒对被领养动物设置时间计划到点时自动生成回访任务提醒志愿者执行数据报表可视化用Vue中的ECharts组件把各月救助量、领养成功率做成图表方便管理员掌握整体趋势。有一次志愿者跟我说以前他们处理一只流浪猫的流程大概要三四天因为信息在每个环节都要重新核对一遍、等待有人看到群消息。系统上线之后从上报到完成基本能在一个工作日内走完。听着好像只是效率提升了一点实际上这意味着这只猫少受了好几天苦这条领养信息的及时流转可能直接决定它能不能更早找到一个家。如果你也正在用SpringBootVue这套组合做管理系统希望这篇拆解能给你一些可以直接落地的参考。我最大的体会是技术本身不复杂真正花时间的往往是把业务流程问清楚、把边界场景想明白。把这些前期功夫做扎实了后面coding自然就顺了。
返回列表