基于Spring Boot与微信小程序的宠物领养医院系统设计与实现
发布时间:2026/9/16 13:36:27来源:尧图网络
简介基于 Java 与 Spring Boot 的微信小程序宠物领养医院系统同时包含网页管理端与小程序端源码面向计算机相关专业学生和 Java 技术学习者适用于课程设计、期末大作业与毕业设计等场景。项目已通过严格调试具备一定 Java 基础即可运行与二次开发。压缩包共 543 个文件体积约 38.04MB90 个 Java 文件构成后端服务90 个 JS 与 70 个 TS 负责前端逻辑wxml/wxss/json 等小程序页面文件覆盖领养、预约、医院管理等模块另有 HTML 管理页面、XML 配置、PNG 图片素材及项目说明文档目录结构清晰。目前已有 164 人学习使用。借由完整源码与说明文档读者可以掌握 Spring Boot 接口开发、小程序端页面构建、前后端数据交互以及宠物领养医院业务模块的实现思路也能对照项目梳理从数据库配置到界面展示的完整流程是一份适合综合实践的项目参考资料。1. 宠物领养医院系统的边界领养、门诊和审核入口其实是一条状态链宠物领养和门诊在界面上看着不相关底层却共享同一份宠物档案这也是基于 Java Spring Boot 的微信小程序宠物领养医院系统最容易让人误判的点它不是简单的宠物展示加报名表单而是一条从建档、体检、领养申请、审核到归档的状态链。网页管理端负责审核和数据维护小程序端负责浏览、申请和预约两端操作的是同一张宠物表。对要交毕设或做外包交付的开发者这套源码和项目说明最值得拆的是三件事领养申请状态字段怎么设计、Spring Boot 接口如何不重复发宠物、小程序端怎么接登录态和表单提交。页面可以慢慢调状态链错一位管理端和用户端就会对不上。2. 数据建模先给领养流程定状态机再写建表 SQL接手这类宠物领养系统我一般不会先打开页面看功能而是先画一张状态流转图。原因是领养有“用户申请、管理员审核、线下回访、健康记录补录”多个动作状态不先定清楚后面的表结构、接口参数、管理端列表筛选全都会跟着改。2.1 领养流程的五个状态与三个关键动作宠物状态放在 pet 表里用 TINYINT 表示它不单是页面上的一个标签还决定列表页能否展示、申请接口是否放行。这里定义的状态集是状态值状态含义触发动作列表可见性0待体检管理员创建档案不可见1待领养体检通过小程序可见2申请中用户提交申请详情页锁定3待回访管理员审核通过仅管理端4已领养回访完成只读归档为什么要五个状态而不是三个因为“已领养”不是审核通过瞬间产生的中间还有线下回访回访失败需要退回“待领养”退回时申请记录不能删除要留痕。这五个状态对应到页面上就是管理端列表的五个筛选项少一个字段审核进度就跟不上。2.2 三张核心表的最小建表 SQL宠物档案表 pet、领养申请表 adoption_application、门诊记录 medical_record三张表的关系是 pet 一对多另外两张表。CREATE TABLE IF NOT EXISTS pet ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, species VARCHAR(20) NOT NULL COMMENT CAT/DOG, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-待体检 1-待领养 2-申请中 3-待回访 4-已领养, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_name_species (name, species) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宠物档案表; CREATE TABLE IF NOT EXISTS adoption_application ( id BIGINT PRIMARY KEY AUTO_INCREMENT, pet_id BIGINT NOT NULL, user_id BIGINT NOT NULL, status TINYINT NOT NULL DEFAULT 2 COMMENT 2-申请中 3-待回访 4-已领养 5-已拒绝, reason VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_pet_status (pet_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT领养申请表;pet 表状态字段用 TINYINT状态含义靠注释维护而不是用字符串。字符串状态在列表排序和索引上不如整型而且状态先定义好再在代码里写映射枚举不会出现“已领养”和“领养完成”这种同义不同名的脏数据。adoption_application 表独立于 pet 表是因为一次申请是一个业务动作需要记录申请理由和申请时间不能只改一个状态字段。索引 idx_pet_status 保证后面防重复申请的查询走索引。medical_record 表通过 pet_id 关联 pet只负责在宠物“待回访”到“已领养”阶段补录体检和疫苗信息状态不对时补录接口应拒绝写入。2.3 springboot MyBatis 自动建表的配置边界经常有人在 springboot 项目里把建表 SQL 丢进 schema.sql配了spring.sql.init.modealways本地第一次启动成功第二次启动直接报错。原因比较常规spring.sql.init不是 MyBatis 的能力它是 Spring Boot 的脚本初始化器默认每次启动都会执行 schema-locations 指定的脚本脚本里如果写的是CREATE TABLE第二次必然失败。spring: datasource: url: jdbc:mysql://localhost:3306/pet_hospital?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 sql: init: mode: always schema-locations: classpath:db/schema.sql continue-on-error: false两个注意点schema.sql 里必须统一写成CREATE TABLE IF NOT EXISTS配合 modealways 才能反复启动不报错生产环境建议把 mode 改成never表结构迁移交给 Flyway 或 Liquibase。如果项目同时用了mybatis-spring-boot-starter初始化脚本会在数据源创建后执行但 MyBatis 的 Mapper XML 里引用了不存在的表服务起来后第一次查询才会报错所以自动建表只能解决“表有没有”不解决“字段对不对”。3. Spring Boot 后端领养申请接口、状态流转与登录态后端部分需要优先实现的只有三个接口登录、宠物列表、提交领养申请。其余的诊疗记录、回访记录都属于常规增删改查顺着这三条链路补就行。3.1 提交领养申请的表单参数与后端必查项前端上传的领养申请一般是这样的 JSON{ petId: 12, reason: 家里有院子全天有人照顾, hasExperience: true, ownerType: 1 }参数类型必填后端校验petIdLong是必须存在且状态为 1待领养reasonString是长度 5-200不通过直接拒绝hasExperienceBoolean否不影响申请仅作为审核参考ownerTypeInteger是1-本人领养 2-代家人领养petId 和 ownerType 不能只依赖前端传来的值。小程序页面可以改参数所以后端要从登录态里取 userIdpetId 则用SELECT ... FOR UPDATE锁行后再判断状态避免两个人同时点申请同一只宠物。3.2 状态机实现事务加行锁防重复领养Service public class AdoptionService { Transactional(rollbackFor Exception.class) public void apply(ApplyRequest req, Long userId) { Pet pet petMapper.selectByIdForUpdate(req.getPetId()); if (pet null || !Integer.valueOf(1).equals(pet.getStatus())) { throw new BizException(宠物不在可领养状态); } int applying applicationMapper.countByPetIdAndStatus(req.getPetId(), 2); if (applying 0) { throw new BizException(该宠物已被申请先到先得); } petMapper.updateStatusById(req.getPetId(), 2); applicationMapper.insert(req.getPetId(), userId, req.getReason(), req.getHasExperience() ? 1 : 0, req.getOwnerType(), 2); } }selectByIdForUpdate对应 Mapper 里的SELECT * FROM pet WHERE id #{petId} FOR UPDATE在事务内把这一行锁住第二个请求会等锁而不是直接读到旧状态。先锁行、再查是否已有申请中的记录、最后插入申请这三个动作的顺序不能换否则两个用户可能在“查询无申请”之后同时插入出现两笔申请对应一只宠物。pet 表和 adoption_application 表的状态都改成 2保证宠物详情页显示“申请中”和管理端列表筛选项一致。Transactional必须加 rollbackFor Exception.class否则业务异常默认不触发事务回滚会留下“申请记录没了但宠物状态变成申请中”的脏数据。3.3 微信登录态code 换 openid 的最小实现小程序端wx.login()会拿到一个临时 code有效期只有五分钟。后端拿着 code 调微信的jscode2session接口换 openid 和 session_key。openid 是用户唯一标识业务表里的 user_id 可以存 openid也可以存一个由 openid 映射出来的自增主键。public String code2Session(String code) { String url https://api.weixin.qq.com/sns/jscode2session ?appid appId secret appSecret js_code code grant_typeauthorization_code; RestTemplate rest new RestTemplate(); MapString, Object result rest.getForObject(url, Map.class); if (result null || result.get(openid) null) { throw new BizException(code 已过期请重新授权); } String openid result.get(openid).toString(); return JwtUtil.createToken(openid); }appid 和 appsecret 在微信公众平台的“开发管理 - 开发设置”里拿secret 不能写进小程序源码只存在后端配置里。jscode2session 是 GET 请求拼接参数时 js_code 要做 URL 编码否则特殊字符会直接报 400。老项目里经常有人拿 session_key 当 token这是不对的session_key 是解密手机号和用户敏感数据的密钥不能下发到前端因此这里用 JWT 自己签发登录态JwtUtil 用 jjwt 或 hutool 实现即可过期时间按业务场景定小程序端通常设 7 天。4. 微信小程序端登录态携带、申请表单与导航栏适配小程序端代码量最大的不是页面而是三个横切问题每个请求怎么带上 token、表单校验放在哪一层、自定义导航栏在不同机型上的高度。4.1 把 wx.request 包成 Promise统一处理 401原生的wx.request走回调页面里每调一次接口就要写一层 success/fail业务一多到处是重复代码。常见做法是抽一个 request.js把 wx.request 包装成 Promise并在拦截层统一处理登录失效const BASE_URL http://192.168.1.10:8080; // 改成电脑局域网 IP function request(url, options {}) { const { method GET, data {}, needAuth true } options; return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method, data, header: needAuth ? { Authorization: Bearer wx.getStorageSync(token) } : {}, success(res) { if (res.statusCode 401) { wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/index }); reject(new Error(登录已过期)); return; } if (res.data res.data.code ! 0) { wx.showToast({ title: res.data.msg, icon: none }); reject(new Error(res.data.msg)); return; } resolve(res.data.data); }, fail() { wx.showToast({ title: 网络异常, icon: none }); reject(new Error(网络异常)); }, }); }); } module.exports { request, BASE_URL };401 是后端 JWT 过滤器抛出的未登录信号前端在响应层统一清 token 并跳登录页不用每个业务页面都去判断 errCode。code 是后端业务返回码约定 0 表示成功页面层拿到 data 直接用失败信息在拦截层 toast 出来。needAuth 参数用来区分登录接口本身登录接口不能带着旧 token 去调也不能在 401 时再跳一次登录页。4.2 领养申请表单的校验分工与防重复提交小程序的表单页一般用 form 组件收集字段bindsubmit里做必填校验。前端校验只负责体验后端校验才是最终防线两边的分工要明确提交内容前端处理后端处理petId从详情页传入页面不展示查状态是否为待领养reason必填带字数统计长度校验过滤空白字符串ownerTypepicker 选择枚举校验不在集合内拒绝hasExperienceswitch 组件转成 0/1 存库我一般会在表单页加一个submitting标志位提交后置 true接口成功或失败后置 false防止用户连续点击提交按钮给同一只宠物发请求。后端已经用 FOR UPDATE 锁了行前端防重复只是减少无效请求并不能替代后端校验。4.3 顶部导航栏高度适配取 statusBarHeight 而不是写死数值如果项目用了自定义导航栏状态栏高度的取数方式要注意。老代码用wx.getSystemInfoSync().statusBarHeight新基础库推荐用wx.getWindowInfo()基础库版本过低时需要做一次回退function getNavBarHeight() { const info wx.getWindowInfo ? wx.getWindowInfo() : wx.getSystemInfoSync(); const statusBarHeight info.statusBarHeight; return statusBarHeight 44; // 44 是胶囊按钮区域常规高度 } Page({ data: { navBarHeight: 0 }, onLoad() { this.setData({ navBarHeight: getNavBarHeight() }); }, });44是胶囊按钮加上状态栏的常规高度单位 px。iPhone 全面屏和 Android 的 statusBarHeight 数值不同所以不能写死 64 或 88。页面顶部如果直接放position: fixed的导航栏需要把页面内容的 padding-top 设为 statusBarHeight 44否则内容会被盖住。自定义导航栏的收益是页面看起来更干净代价是每个页面都要传高度。5. 部署与联调启动服务后先过这三个检查点5.1 小程序如何连到本地后端小程序真机预览时localhost 指向的是正在运行的手机不是开发电脑。把 BASE_URL 换成电脑的局域网 IP比如http://192.168.1.10:8080然后在微信开发者工具右上角“详情 - 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”本地调试就能直接访问 http 地址。这个选项只对开发版和体验版生效上线前必须换成正式 HTTPS 域名并在小程序后台配置 request 合法域名。5.2 用 Burp Suite 验证登录态与领养申请链路抓包不只是看接口通不通而是验证参数是不是真的按预期传递。电脑打开 Burp Suite默认监听 127.0.0.1:8080把手机连到和电脑相同的局域网在 Wi-Fi 设置中填写电脑的 IP 和 8080 监听端口手机上操作一遍领养申请流程然后在 HTTP History 里按三个顺序查登录接口响应里是否返回了 token后续每个请求的 Authorization 头是否带上该 token提交领养申请时 petId、ownerType 等参数是否与页面操作一致。第三项最容易被忽略。如果小程序端把 petId 放在 storage 里而请求直接读 storage抓包时看到的动态参数就可能与页面展示不一致这就是联调阶段最值得提早发现的问题。5.3 Spring Boot 版本、支付回调与数据库编码的三个启动排查点Spring Boot 版本太高会带来一个明显的迁移问题3.x 把命名空间从 javax 换成 jakarta老代码 import 直接报红。选版本前先看 JDKJDK 17 对应 Spring Boot 3.xJDK 8 就停留在 2.7。微信支付 v3 回调要求接口返回{code: SUCCESS, message: 成功}只返回 200 不够微信会重复通知回调里还要验签不能只看 out_trade_no 就修改订单状态。数据库中文乱码则检查连接串是否带characterEncodingutf8建表统一用 utf8mb4否则小程序提交的中文申请理由入库后可能变成问号。联调阶段遇到接口返回 200 但页面没反应先看控制台和 Network 面板里 res.data.code 是不是非 0这比逐行断点更快定位问题。本文还有配套的精品资源点击获取
网站建设高端定制企业官网