ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Android宠物领养APP开发全流程:从架构到联调避坑指南

SpringBoot+Android宠物领养APP开发全流程:从架构到联调避坑指南 说实话这段时间帮几个学弟学妹把控计算机毕业设计我连续撞上了同一个高分方向——SpringBootAndroid的宠物领养APP。乍一看这题目挺常规一个后端写接口一个客户端调接口再套上前后端分离架构的名头好像就能交差了。可真上手去做才发现从流浪动物救助与领养的业务建模到移动端的网络调试、状态流转、图片存取坑远比想象中多。特别是寻主和领养这两类场景在数据模型上要怎么统一很多文章都没讲透。这篇文章我想把整个基于SpringBoot与Android的移动端宠物寻主与领养系统从设计到落地的完整链路拆开聊一聊。里面会包含我实际带队做这个项目时的分层思路、核心表结构、接口约定、Android客户端封装方式以及一批只有真正跑过联调才会踩到的坑。无论你是准备拿它当毕业设计、课设还是单纯想做一个练手项目这篇文章应该能帮你少走至少一周的弯路。做完整个项目你再回头看会觉得这个题目的价值不在新技术上而在于它逼迫你把一个完整的业务闭环真正跑通用户注册登录、发布宠物、浏览筛选、提交领养申请、审核状态流转、走失与拾获登记、认领匹配——每一环都藏在前后端配合的细节里。1. 先把需求看清宠物领养不是普通增删改查1.1 场景拆解救助、领养、寻主到底包含哪些业务很多同学拿到这个题目第一反应是不就是发布宠物 列表展示 申请领养嘛结果做完一轮就被老师问住了——流浪动物救助和宠物寻主根本不是同一个玩法。我习惯把这个系统拆成三类核心场景第一类是流浪动物救助与领养。流浪动物被救助后救助人需要登记宠物基本信息品种、年龄、毛色、健康状况、是否绝育、疫苗情况上传照片设置领养条件如仅限本地需接受回访然后等普通用户来提交领养申请。救助人或管理员审核申请通过后双方线下见面、完成送养系统里还要记录领养状态。第二类是宠物寻主。这里面又分两种子场景一种是用户自己的宠物走失了用户发布寻主启事其实就是寻宠物写明特征、走失地点、时间、酬谢方式另一种是用户在外面捡到了疑似走失的宠物发布拾获登记留照片、留拾获位置。系统需要支持这两种信息之间按品种、颜色、地区做模糊匹配把可能相关的走失和拾获信息相互推荐。第三类是平台基础能力。用户注册登录、个人信息管理、收藏、浏览记录、领养申请进度查询、系统消息通知还有最容易被忽略的管理后台——管理员需要能审核宠物发布内容、管理用户、下架违规宠物信息。想清楚这三层你才能回答这个系统的核心创新点是什么。如果只做一套普通的CRUD答辩时连第二个问题都撑不过去。1.2 为什么是 SpringBoot Android 前后端分离这个组合说句实话是毕业设计里稳妥性与工作量平衡得最好的方案之一。SpringBoot后端选型的理由很实在它内置Tomcat、自动配置、生态成熟MyBatis-Plus 或者 Spring Data JPA 可以省掉大量单表CRUD代码。对于一个以业务逻辑和流程为主的系统SpringBoot 能让你把精力放在流程怎么走而不是框架怎么配上。而且答辩时老师对这个技术栈的接受度很高不会被追问太多冷门问题。Android原生客户端选Java/Kotlin是因为移动端的独立性。这个题目明确要求做APP那就用原生来实现配合RecyclerView、Retrofit、Glide这几个库就足够完成信息流、图片加载和网络请求。前后端分离架构在这里不是噱头而是有实际意义的后端只提供RESTful APIAndroid端通过HTTP/HTTPS调用两边可以并行开发、单独测试。这个架构还能顺带导出接口文档比如用knife4j或springdoc答辩时展示分工界面会非常好看。1.3 角色与模块划分分清谁在用什么功能项目里我建议至少设计三种角色普通用户、救助/送养人可并入普通用户升级权限、管理员。为了简单也可以采用普通用户 管理员双角色让普通用户同时拥有发布走失发布拾获提交领养申请的权限管理员负责审核和数据管理。模块上这样分用户模块注册、登录、找回密码、个人资料、我的发布、我的申请宠物模块宠物信息发布、宠物列表按品种/地区/状态筛选、宠物详情、图片轮播领养模块提交领养申请、审核申请、领养状态流转待审核→已通过/已拒绝→确认领养→完成寻主模块走失信息发布、拾获信息登记、智能匹配推荐、认领确认救助模块救助记录登记救助人、时间、地点、救助经过、医疗处理情况消息模块申请审核结果通知、匹配成功提醒这样划分以后前后端的接口边界就非常清晰了。后端按模块建包建表Android端按模块建页面哪怕两个人协作也不会互相等着。2. 后端SpringBoot 这样做既稳又好答辩2.1 分层结构是最容易被低估的得分点后端结构我推荐这样做基本是行业里最常见的规范com.example.petadoption ├── controller # 接口层只做参数接收与响应包装 ├── service # 业务层核心逻辑与事务 ├── mapper # 数据访问层MyBatis-Plus 的 Mapper ├── entity # 实体类 ├── dto # 请求/响应对象不要直接用实体接收参数 ├── common # 统一返回结果、全局异常处理、常量 ├── config # 配置类跨域、拦截器、静态资源映射 ├── utils # JWT工具、文件上传工具、时间工具 └── PetApplication.java这里要特别强调一点接口入参不要直接用Entity。很多同学图省事让Controller直接接收Entity对象结果前端传什么都能往数据库里塞状态字段、用户ID都能被客户端篡改。正确的做法是定义PetPublishDto、AdoptionApplyDto这类DTO只接收业务需要的字段service层再转成Entity保存。这不仅安全答辩时也是加分点老师一眼就看出你懂分层。2.2 核心数据表设计一张表一个关注点表的设计决定了整个项目的表达能力。我按模块整理了一份核心表清单字段我只挑关键的讲表名核心字段状态字段设计userid、phone、password、nickname、avatar、rolerole0普通 1管理员petid、user_id、name、category、breed、gender、age、color、health、vaccine、address、description、images、statusstatus0待审核 1展示中 2已领养 3已下架adoption_applicationid、pet_id、user_id、apply_reason、contact_phone、contact_address、status、create_timestatus0待审核 1已通过 2已拒绝 3已取消 4已完成lost_reportid、user_id、pet_name、category、breed、color、lost_address、lost_time、feature、reward、contact、images、statusstatus0寻宠中 1已找到found_reportid、user_id、category、breed、color、found_address、found_time、feature、contact、images、statusstatus0待认领 1已认领rescue_recordid、user_id、pet_id、rescue_address、rescue_time、medical_desc、remark关联宠物与救助记录几个设计上的细节我要单独说状态字段一律用数字或字符串枚举不要直接存已领养这种中文。否则后面你要统计领养转化率各状态下宠物数量时SQL 会写到怀疑人生。用数字 常量类或枚举类前端再映射成文案展示。images字段可以存逗号分隔的URL字符串因为一个宠物最多上传3~5张图没必要单独建一张图片表。但如果做管理后台要大量查询也可以规范化为 pet_image 子表这个看工作量取舍。所有表保留create_time和update_time用 MyBatis-Plus 的自动填充减少重复代码。adoption_application要加pet_id和user_id的联合唯一索引吗我建议加但要注意业务允许不允许同一个人反复申请同一只宠物。明确一个用户只能申请一次失败后可再次申请时前端在提交前就做校验后端也得兜底。2.3 登录认证JWT 方案如何落地毕业设计级别用JWT最合适无状态、不需要在服务端存Session、Android端存好token每次请求塞到Header里就行。具体做法用户登录时后端校验手机号密码生成JWT返回payload里放userId、role设置过期时间我一般设7天毕业设计演示够用。定义一个拦截器AuthInterceptor拦截除了login、register、宠物列表等公开接口以外的路径。从请求头Authorization取 token解析失败抛401解析成功把 userId 放进ThreadLocal或请求上下文。Android 端在OkHttp的Interceptor里统一往请求头塞 token这样所有请求都无需单独处理认证。密码存储方面别用明文。最简单可靠的是BCrypt加密Spring Security 的BCryptPasswordEncoder可以单独拿出来用不引整个Security也行。2.4 图片上传与静态资源映射细节决定演示效果宠物领养APP里图片是灵魂但图片这块往往坑最多。后端建议本地上传静态资源映射的方案。在application.yml里配spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB custom: upload-path: ./upload/再写一个上传工具类用UUID重命名文件避免中文和重名问题按日期分目录存储然后返回可访问的URL。关键是要加一个WebMvcConfigurer配置类把本地磁盘路径映射为URLConfiguration public class WebConfig implements WebMvcConfigurer { Value(${custom.upload-path}) private String uploadPath; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceResolver(new PathResourceResolver()) .addResourceLocations(file: uploadPath); } }这里有个实战教训路径拼接里的file:前缀必须带Windows 和 Linux 下路径分隔符不一样所以最好的方式是后端提供一个FileUtils.getAbsolutePath()方法统一处理。还有返回给Android端的图片URL一定要用完整路径IP端口路径不能只给/upload/xxx.jpg。3. Android端代码别全塞进Activity架构先立起来3.1 客户端架构MVVM 是省心之选Android 端如果不用架构最后所有逻辑都会堆进Activity一到联调阶段就炸——回调嵌套混乱、状态不同步、一个页面几百行。我建议直接用MVVM也就是ViewModel LiveData Repository这套组合。Android Studio 里建议创建项目时选 Java稳妥资料多或 Kotlin更现代。这个项目客户端重点模块就这几个网络层Retrofit OkHttp Gson/GsonConverter图片加载Glide页面RecyclerView CardView SmartRefreshLayout做下拉刷新/加载更多数据库如果需要本地收藏用 Room否则 SharedPreferences 存 token 就够了3.2 网络层封装统一 BaseUrl、统一返回体、统一异常客户端网络层是整个APP的命脉我一般这样封装定义一个ApiResultT类字段和后端统一返回体对齐code、message、data。用Retrofit.Builder().baseUrl(BASE_URL)创建接口代理。BASE_URL 这里是个大坑见下一条。OkHttp添加两个拦截器一个AuthInterceptor塞token一个LoggingInterceptor打印日志联调必备。定义一个NetworkCallback包装retrofit2.Callback在onFailure里统一处理网络异常Toast 提示网络连接失败onResponse里解析codecode401时跳转登录页其他code直接抛业务错误。BASE_URL 的选择Android 模拟器里访问宿主机要用http://10.0.2.2:8080/真机要用http://电脑局域网IP:8080/。不能用localhost因为手机上的 localhost 指的是手机自己。这个错误几乎每个新手都会犯。3.3 核心页面与交互流程客户端页面我按主线整理几个关键实现点首页RecyclerView展示宠物卡片卡片上显示宠物封面图、名称、品种、状态标签待领养/已领养。用SmartRefreshLayout实现分页加载下拉刷新列表。宠物详情页顶部用ViewPager2做图片轮播下方展示基本信息、健康描述、救助故事。根据pet.status判断按钮是申请领养还是已领养。这里要注意已登录用户才能点申请领养未登录要跳转登录页。发布宠物页表单 图片选择。图片选择用系统相册或相机拿到Uri后先压缩再上传上传走独立的接口成功后把返回的图片URL作为表单字段提交。寻主匹配页输入走失/拾获信息后后端返回可能匹配的宠物列表客户端用列表展示给每条匹配记录提供联系TA按钮。3.4 Glide 加载图片与 Android 明文HTTP配置Glide 的使用很简单Glide.with(context) .load(pet.getImages().get(0)) .placeholder(R.drawable.ic_placeholder) .error(R.drawable.ic_error) .into(imageView);但有两个必须提前处理的点第一AndroidManifest.xml要声明网络权限uses-permission android:nameandroid.permission.INTERNET/。第二如果后端接口是http://明文Android 9API 28开始默认禁止明文流量。最简单的处理办法是在application标签上加android:usesCleartextTraffictrue。如果项目将来要上架建议用networkSecurityConfig精确允许指定域名明文但做课设毕设直接用前者就好省事。4. 前后端联调接口规范定了事情就成了一半4.1 接口约定RESTful 统一返回体 分页参数前后端分离的核心是接口约定。我定义一个统一返回体后端所有接口都这么返回{ code: 200, message: success, data: { } }code200表示成功400表示参数错误401表示未登录或token过期500表示服务器异常。Android端拿到code后统一处理业务逻辑集中在data里。接口按REST风格设计我列几个核心接口作为参考功能接口方法说明用户注册/api/user/registerPOST手机号、密码、验证码用户登录/api/user/loginPOST返回token和用户信息宠物列表/api/pet/listGET分页、按品种/状态筛选发布宠物/api/pet/publishPOST表单图片URL数组宠物详情/api/pet/{id}GET图片、状态、救助记录提交领养申请/api/adoption/applyPOSTpetId、申请理由、联系方式查询我的申请/api/adoption/myGET我的申请列表审核申请/api/adoption/reviewPOST申请id、通过/拒绝、审核备注发布走失信息/api/lost/publishPOST走失宠物信息发布拾获信息/api/found/publishPOST拾获宠物信息寻主匹配/api/match/searchGET按品种/颜色/地区匹配分页参数统一叫pageNum和pageSize返回体里 data 包两个字段total和list。Android端根据total currentSize判断是否还有下一页。4.2 联调阶段最崩溃的几个问题联调阶段我几乎每次都会遇到全部记录下来模拟器访问不到后端宿主机用10.0.2.2代替localhost这是Android模拟器的固定约定。如果还不行检查后端是否启动在0.0.0.0而不是127.0.0.1。真机访问不到后端手机和电脑必须连同一个WiFi后端启动后电脑防火墙要放行8080端口。Windows 下经常需要手动添加入站规则或者临时关防火墙测试。HTTP 明文被拦截上一条说的usesCleartextTraffic。端口被占用SpringBoot 的8080被其他程序占了启动直接报错。改端口用server.port8081改完Android端的BASE_URL也要同步改。时间格式解析不了后端返回LocalDateTime序列化成2025-01-16T10:20:30Gson 解析可能失败。统一在Spring里配置Jackson格式spring.jackson.date-formatyyyy-MM-dd HH:mm:ss同时在Android端给时间字段加SerializedName和自定义解析器或者后端直接把时间转好字符串再返回。4.3 跨域问题要不要处理原生Android App 其实不涉及浏览器同源策略所以联调时不会出现跨域问题。但有两个例外如果你在客户端用了WebView加载管理页面或者后端将来要支持Web管理端那么CorsConfig还是要配。我建议顺手配一个全局跨域成本极低。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(*) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }5. 核心模块实现复盘从发布到领养的完整链路5.1 宠物发布到首页列表一条让人头秃却又最简单的链路先说最简单的链路发布宠物 → 首页展示。Android端流程用户填写宠物表单 → 选择图片 → 图片先上传到/api/upload/image→ 后端返回图片URL → 表单连同图片URL数组一起提交到/api/pet/publish→ 后端校验登录状态和参数 → 保存pet记录status置为0待审核→ 返回发布成功。这里有两个细节第一图片上传和宠物信息提交是两步。图片上传接口在用户还没填写完表单时就可以先调这样体验更好也避免了一张大图卡住整个表单提交。我一般会在onActivityResult拿到图片后立即调上传接口然后把返回URL先积累到一个列表提交表单时一次性带上。第二管理员审核逻辑。宠物发布后status0首页列表查询只查status1展示中。这就在前端体现为我发布了但是首页看不到需要做我的发布页面查看自己的宠物审核状态。这个流程至少会有用户、管理员两端的操作演示起来内容也丰富。首页列表后端Service层大致逻辑public PageResultPetVO getPetList(int pageNum, int pageSize, String category, String city) { LambdaQueryWrapperPet wrapper new LambdaQueryWrapper(); wrapper.eq(Pet::getStatus, 1) .eq(StringUtils.isNotBlank(category), Pet::getCategory, category) .eq(StringUtils.isNotBlank(city), Pet::getCity, city) .orderByDesc(Pet::getCreateTime); PagePet page petMapper.selectPage(new Page(pageNum, pageSize), wrapper); // 转 VO把 images 逗号字符串拆成 ListString }5.2 领养申请状态机用一张表表达完整流程领养申请是整个项目业务逻辑最核心、最值得在答辩时展开讲的部分。它的状态流转是这样的用户提交申请status0 待审核 ├─ 管理员/救助人通过status1 已通过→ 用户确认领养status4 已完成 ├─ 管理员/救助人拒绝status2 已拒绝 └─ 用户自己取消status3 已取消Android端在我的申请里根据status显示不同按钮和文案待审核显示审核中标签已通过显示确认领养和联系送养人已完成显示领养完成已拒绝显示被拒绝。后端在提交申请时要多做三件事校验宠物状态必须为1展示中否则直接返回该宠物不可申请。校验当前用户不是宠物发布者本人自己的宠物不能申请领养。防重复提交同一个人对同一只宠物只能有一条status IN (0,1)的记录否则就提示您已申请过该宠物。第3点非常重要我见过很多项目在这里没拦住导致用户疯狂点了十几次申请列表里全是重复记录。5.3 寻主与拾获匹配这个功能最能体现设计能力寻主部分要让功能看起来智能化其实关键就在匹配算法。我的实现思路是用户发布走失信息时填category猫/狗、breed品种、color毛色、lost_address走失城市。用户发布拾获信息时填同样的字段。匹配接口/api/match/search接受一个参数——到底是找疑似我走失的宠物还是疑似这只走失宠物的拾获信息。后端用Java 8 Stream 或SQL做模糊匹配匹配优先级为品种一致 毛色一致 城市一致 时间相近。匹配结果按得分倒序返回给Android端展示相似度标签品种匹配地区匹配。实际编码时最简单的做法是先把同城市的候选集查出来再用simHash做一次轻量模糊匹配。但毕业设计用不到那么复杂SQL里LIKE拼多个条件再加一个布尔字段标记匹配类型就完全够了。这个功能做得好的话答辩时可以清晰地讲出走失登记和拾获登记两种数据的价值——它们不是孤立的两个模块而是通过匹配引擎产生连接的这也是题目里宠物寻主系统这个词的落点。5.4 消息通知状态变更怎么让用户及时知道消息通知我建议用最轻量的方案申请审核通过/拒绝后后端在状态变更的同一个事务里插入一条message记录Android端在我的消息页面拉取未读消息列表。这样不用引入WebSocket或推送SDK极光推送、个推等演示效果和闭环性也足够。实现细节message表字段id、user_id、type、content、is_read、create_time。当管理员审核申请时Service层调messageService.notify(userId, 您的领养申请已通过审核...)。Android端进入消息页面时请求未读接口并用BadgeView在底部导航栏显示未读角标。如果想让体验更好可以在Android端加一个定时器每30秒轮询未读数量但这个在毕业设计里可选不是必要项。6. 常见问题排查与避坑速查表6.1 后端常见问题症状原因解法8080端口被占用其他程序占用改端口或kill占用进程数据库中文乱码连接串没指定字符集JDBC URL加characterEncodingutf8库表和连接串都统一utf8mb4MyBatis-Plus自动填充不生效缺少TableField(fill FieldFill.INSERT)在实体字段上声明配置MetaObjectHandlerLocalDateTime返回格式是数组Jackson配置问题配置spring.jackson.date-format或实体加JsonFormat上传的图片重启后丢失本地路径存储做好目录规划或改OSS但毕设不需要这里重点提醒一下SpringBoot 版本问题现在下载SpringBoot Init时默认是3.x而3.x要求JDK 17很多同学电脑装的是JDK 8一启动就报错。做毕业设计我强烈建议用2.7.18 JDK 8 的组合资料多、插件兼容性好、遇到问题百度一搜全是答案。如果用3.x注意原来javax.*的包全部变成了jakarta.*很多老教程代码不适用。6.2 Android端常见问题症状原因解法网络请求一直失败BASE_URL地址错误/防火墙拦截用10.0.2.2或局域网IP检查防火墙图片加载不出来明文HTTP被禁止加usesCleartextTraffictrue图片上传后预览成横的未处理 Exif 旋转信息用Glide或自定义工具读取Exif并转正真机安装APK后无法连接后台手机与电脑不在同一网段连同一个WiFi后端监听0.0.0.0列表只加载一页分页参数传错检查pageNum、pageSize参数名是否与后端一致Token过期后页面无反应未统一处理401全局拦截401并跳登录6.3 答辩与演示前必做的几件小事根据我帮人调试的经验演示翻车大多不是技术问题而是准备不足预置数据一定要多。至少放20只不同品种、不同状态的宠物3条走失信息、3条拾获信息。现场现发现传图容易卡顿。网络环境要提前确认。演示用的WiFi下手机和电脑IP要互ping通。很多学校教室的WiFi是AP隔离的手机和电脑根本不在同一网络这时候建议直接用模拟器模拟器走10.0.2.2不受影响或者提前把后端部署到云服务器。图片别用太大。手机拍的照片动辄5MB上传慢还占服务器空间。Android端在上传前做一次压缩压缩到800px宽、80%质量就够展示用。准备一份接口文档。答辩时老师可能会抽查某个接口设计直接用springdoc-openapi或knife4j生成Swagger UI打开浏览器现场展示接口列表和数据字段非常加分。6.4 一点关于工作量的建议这个项目如果只是宠物信息发布 领养申请工作量明显偏少。想让系统饱满我建议至少补上寻主与拾获双通道、领养审核状态机、管理端审核流、消息通知。这四块加起来代码量不大但能极大丰富业务故事线。尤其是宠物寻主——走失报告与拾获报告互相匹配推荐这个功能建议一定要做它是题目里最亮眼的点也是很多范文根本没做透的点。写到最后说点实在话带完这套项目我最深的体会是毕业设计重要的不是写了多少行代码而是你有没有把一个业务闭环真正跑通。哪怕技术栈平平无奇但只要用户发布宠物 → 管理员审核 → 首页展示 → 他人申请 → 状态流转 → 线下领养 → 寻主匹配每一个环节都经得起追问答辩基本就稳了。如果让我再提一条实打实的建议从第一天就把联调当成开发的一部分而不是最后一步。后端每写完一个接口立刻用Postman测一遍Android端每写完一个页面立刻连本地后端验证一遍。拖到最后再联调永远会爆发连环问题而且你会分不清到底是谁的锅。这个项目后续还能很自然地扩展成微信小程序版、管理后台Web版、甚至对接地图API做周边救助站定位。先把这一版稳稳做完你会发现后面这些都只是量的叠加没有任何新的坎了。祝顺利。
返回列表