news 2026/10/6 1:18:47

零食商城毕设实战:Android Studio+Spring Boot全栈开发与接口联调避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
零食商城毕设实战:Android Studio+Spring Boot全栈开发与接口联调避坑指南

简介:这份资源是一篇基于Android Studio开发的零食商城APP毕业论文,面向计算机相关专业本科生及移动应用开发初学者,可作为毕业设计选题参考或课程项目实践模板。论文围绕传统零食零售受时空限制、信息获取有限、商家运营成本高等痛点,提出一套移动端商城解决方案,技术栈覆盖Java、Android SDK、Spring Boot与MySQL,并涉及Vue相关技术。压缩包内仅含1个docx文档,约2.67MB,内容包含摘要、绪论、相关技术介绍、系统设计与实现等完整章节,结构规范,可直接用于论文写作参考。文中详细阐述了管理员端的用户管理、活动公告、新闻资讯、商品信息与订单管理,以及用户端的注册登录、商品浏览、点赞收藏、购物车、下单支付与订单取消等功能设计,并配有技术选型说明与数据库设计思路。目前已有46人学习下载,适合需要快速搭建毕设框架、理解前后端分离开发流程的读者借鉴。

1. 零食商城毕设:Android Studio 客户端加 Spring Boot 后端的真实落地路径

很多同学拿到「基于 Android Studio 的零食商城毕业论文」这个题目时,第一反应是去搜一套现成源码改改交差,结果下载下来的项目要么跑不起来,要么技术栈对不上导师要求。我前后带过几届学生的毕设,也帮朋友排查过不少跑不起来的商城项目,最常见的翻车点不是代码写得多烂,而是客户端和后端各写各的,接口对不上、数据库字段对不上、打包出来的 APK 连不上本地服务。这篇笔记就按一条能真正跑通的路线来讲:Android Studio 做买家端,Spring Boot 提供 REST 接口,MySQL 存数据,中间用 Retrofit 或 OkHttp 通信。适合正在做毕设、需要一套能演示、能答辩、代码自己能讲清楚的同学。下面从环境搭建一路讲到接口联调和答辩前怎么自测,每一步都给出可复现的命令和参数。

2. 环境搭建:Android Studio 与 Spring Boot 双端配置

2.1 Android Studio 安装与中文设置

Android Studio 的下载安装本身不复杂,但毕设阶段最容易卡在 Gradle 同步和 SDK 下载上。我一般建议直接用官网当前稳定版,安装时勾选 Android SDK、Android SDK Platform 和 Android Virtual Device 三项。安装完成后第一次启动会提示下载 SDK,如果网络慢,可以在Settings → Appearance & Behavior → System Settings → HTTP Proxy里配置镜像,或者手动在SDK Manager里改下载源。

关于「Android Studio 怎么设置中文」,官方并没有内置中文语言包,网上流传的中文插件大多只翻译了菜单的一部分,装了之后反而容易出现菜单错位。我的建议是不要折腾中文,把常用快捷键记住比汉化有用得多。如果导师明确要求界面中文,可以装官方市场的 Chinese 语言包插件,但答辩前一定要确认不会导致 IDE 崩溃。

新建项目时选Empty Views Activity,语言选 Java,最低 SDK 建议选 API 24(Android 7.0),这样覆盖的设备够多,也不会因为版本太低用不了新库。项目建好后先跑一次模拟器,确认能正常启动再往下做。

2.2 Spring Boot 后端项目初始化

后端用 Spring Boot 2.7.x 或 3.x 都行,但要注意 JDK 版本匹配:Spring Boot 3.x 要求 JDK 17 以上,如果学校机房还是 JDK 8,就老老实实用 2.7.x。用 IntelliJ IDEA 社区版也能开发 Spring Boot,只是没有旗舰版的 Spring 专属支持,装个 Spring Boot Helper 插件就够用了。

初始化时勾选 Spring Web、MyBatis Framework、MySQL Driver 三项。生成的项目结构里,application.properties要改成application.yml方便管理,核心配置如下:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/snack_mall?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.snackmall.entity

serverTimezone必须写,否则 MySQL 8.0 连接会报时区错误;useSSL=false在本地开发时关掉能省去证书配置的麻烦。数据库名snack_mall要提前在 MySQL 里建好,字符集用utf8mb4,不然商品名里的特殊字符会乱码。

2.3 MySQL 安装与建库建表

MySQL 在 Windows 10 上的安装,推荐用官方 Installer,选Developer Default一路下一步。安装过程中会要求设置 root 密码,记牢。安装完成后用命令行验证:

mysql -u root -p

输入密码后进入 MySQL 命令行,执行建库语句:

CREATE DATABASE snack_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE snack_mall; CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT DEFAULT 0, image_url VARCHAR(255), category_id INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total_price DECIMAL(10,2), status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );

product表存零食商品,orders表存订单,status用 0/1/2 表示待付款、已付款、已发货。字段类型别用FLOAT存价格,DECIMAL才不会出现 0.1+0.2 那种精度问题。建完表后往product里插几条测试数据,方便后面接口调试。

3. 后端接口开发:商品、订单与用户三个核心模块

3.1 商品列表与详情接口

商品模块是商城最基础的部分,买家端首页要展示商品列表,点进去要看详情。用 MyBatis 写 Mapper 接口和 XML 映射文件,Controller 层暴露 REST 接口。先定义实体类:

public class Product { private Integer id; private String name; private BigDecimal price; private Integer stock; private String imageUrl; private Integer categoryId; // getter 和 setter 省略 }

Mapper 接口里定义两个方法:

@Mapper public interface ProductMapper { List<Product> selectAll(); Product selectById(Integer id); }

对应的 XML 文件放在resources/mapper/ProductMapper.xml:

<mapper namespace="com.example.snackmall.mapper.ProductMapper"> <select id="selectAll" resultType="com.example.snackmall.entity.Product"> SELECT id, name, price, stock, image_url AS imageUrl, category_id AS categoryId FROM product ORDER BY id DESC </select> <select id="selectById" parameterType="int" resultType="com.example.snackmall.entity.Product"> SELECT * FROM product WHERE id = #{id} </select> </mapper>

注意image_url和imageUrl的映射,如果数据库字段用下划线命名而 Java 用驼峰,要么在 SQL 里写AS别名,要么在application.yml里开启map-underscore-to-camel-case: true。我一般两个都做,防止哪天忘了改配置。

Controller 层:

@RestController @RequestMapping("/api/product") public class ProductController { @Autowired private ProductMapper productMapper; @GetMapping("/list") public Result list() { return Result.success(productMapper.selectAll()); } @GetMapping("/detail/{id}") public Result detail(@PathVariable Integer id) { return Result.success(productMapper.selectById(id)); } }

Result是统一返回格式,包含code、msg、data三个字段,客户端解析起来方便。接口路径统一加/api前缀,后面 Nginx 转发或者改端口时不用动客户端代码。

3.2 订单创建与查询接口

订单接口比商品复杂,涉及事务和库存扣减。创建订单时要做三件事:插入订单记录、插入订单明细、扣减商品库存。这三步必须在一个事务里,否则会出现库存扣了但订单没生成的情况。

@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private ProductMapper productMapper; @Transactional public void createOrder(Integer userId, List<OrderItem> items) { BigDecimal total = BigDecimal.ZERO; for (OrderItem item : items) { Product p = productMapper.selectById(item.getProductId()); if (p.getStock() < item.getQuantity()) { throw new RuntimeException("库存不足"); } total = total.add(p.getPrice().multiply(new BigDecimal(item.getQuantity()))); } Order order = new Order(); order.setUserId(userId); order.setTotalPrice(total); order.setStatus(0); orderMapper.insert(order); for (OrderItem item : items) { item.setOrderId(order.getId()); orderMapper.insertItem(item); productMapper.reduceStock(item.getProductId(), item.getQuantity()); } } }

@Transactional注解要加在 Service 方法上,不要加在 Controller 上。库存扣减的 SQL 用UPDATE product SET stock = stock - #{qty} WHERE id = #{id} AND stock >= #{qty},把库存判断放进 SQL 里,避免并发时超卖。订单查询接口按用户 ID 查,返回订单列表和每个订单的商品明细,客户端用 RecyclerView 展示。

3.3 用户登录与 Token 校验

毕设级别的用户模块不用搞太复杂,手机号加密码登录即可。密码存数据库前用 MD5 加盐,别明文存。登录成功后生成一个 Token 返回给客户端,后续请求在 Header 里带上Authorization: Bearer xxx。

@PostMapping("/login") public Result login(@RequestBody LoginDTO dto) { User user = userMapper.selectByPhone(dto.getPhone()); if (user == null || !MD5Util.verify(dto.getPassword(), user.getPassword())) { return Result.error("账号或密码错误"); } String token = JwtUtil.generateToken(user.getId()); return Result.success(token); }

Token 用 JWT 生成,设置 7 天过期。客户端把 Token 存在 SharedPreferences 里,每次请求通过 OkHttp 拦截器自动加到 Header。后端写一个拦截器校验 Token,放行登录和商品列表接口,其他接口都要校验。这里有个坑:拦截器里不要用@Autowired注入 Bean,用构造函数注入或者从WebApplicationContext里取,否则拦截器可能拿不到 Mapper。

4. 客户端开发:Android Studio 里的页面与网络请求

4.1 商品列表页与 RecyclerView 适配

买家端首页用 RecyclerView 展示商品列表,每个 item 显示商品图片、名称、价格。布局文件item_product.xml里放一个 CardView,里面用 ConstraintLayout 排布 ImageView 和 TextView。Adapter 继承RecyclerView.Adapter,在onBindViewHolder里用 Glide 加载图片:

@Override public void onBindViewHolder(ViewHolder holder, int position) { Product product = list.get(position); holder.tvName.setText(product.getName()); holder.tvPrice.setText("¥" + product.getPrice()); Glide.with(holder.itemView.getContext()) .load(product.getImageUrl()) .placeholder(R.drawable.ic_placeholder) .into(holder.ivProduct); }

Glide 加载网络图片需要加网络权限,在AndroidManifest.xml里声明INTERNET权限。如果图片地址是 HTTP 而不是 HTTPS,还要在application标签里加android:usesCleartextTraffic="true",否则 Android 9 以上会直接拒绝请求。这个坑我见过太多次,客户端一直转圈,查半天才发现是明文流量被拦了。

4.2 Retrofit 网络层封装与接口对接

网络请求用 Retrofit 加 Gson 转换器,先定义 API 接口:

public interface ApiService { @GET("api/product/list") Call<Result<List<Product>>> getProductList(); @GET("api/product/detail/{id}") Call<Result<Product>> getProductDetail(@Path("id") int id); @POST("api/order/create") Call<Result<Void>> createOrder(@Header("Authorization") String token, @Body OrderRequest request); }

Retrofit 实例用单例模式创建,BaseUrl 指向后端地址。模拟器访问本机服务要用10.0.2.2而不是localhost,真机调试则要用电脑的局域网 IP,比如192.168.1.100:8080。这两个地址搞混是新手最常见的翻车点,客户端一直报连接超时,其实服务端日志里连请求都没收到。

public class RetrofitClient { private static final String BASE_URL = "http://10.0.2.2:8080/"; private static Retrofit retrofit; public static Retrofit getInstance() { if (retrofit == null) { OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new TokenInterceptor()) .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .build(); retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit; } }

TokenInterceptor从 SharedPreferences 里读 Token 加到 Header,没有 Token 就跳过。超时时间设 10 秒,太短容易误报,太长用户等得着急。

4.3 购物车与下单流程实现

购物车数据可以存本地 SQLite,也可以存服务端。毕设建议存本地,减少后端接口数量。用 Room 数据库建一个cart表,字段包括商品 ID、名称、价格、数量。购物车页面用 RecyclerView 展示,底部显示合计金额和「去结算」按钮。

点击结算后跳转到订单确认页,把购物车数据转成OrderRequest发给后端。下单成功后清空本地购物车,跳转到订单列表页。这里要注意:下单接口要传 Token,否则后端拦截器会返回 401。我一般会在 Retrofit 的拦截器里统一处理 401,跳转到登录页让用户重新登录,而不是在每个请求回调里判断。

5. 避坑与排查:接口联调中最容易翻车的五个点

5.1 模拟器访问 localhost 返回连接拒绝

现象:客户端请求一直失败,Logcat 里报Connection refused,但浏览器访问localhost:8080是正常的。

原因:Android 模拟器里的localhost指向模拟器自身,不是宿主机。宿主机要用10.0.2.2这个特殊地址。

解决:把 Retrofit 的 BaseUrl 改成http://10.0.2.2:8080/。真机调试则改成电脑的局域网 IP,并确保手机和电脑在同一个 WiFi 下,电脑防火墙放行 8080 端口。

5.2 MySQL 8.0 连接报时区错误

现象:Spring Boot 启动时报The server time zone value '�й���ʱ��' is unrecognized。

原因:MySQL 8.0 默认时区是系统时区,JDBC 驱动解析不了中文时区名。

解决:在 JDBC URL 里加serverTimezone=Asia/Shanghai,或者登录 MySQL 执行SET GLOBAL time_zone = '+8:00';。前者更省事,改配置就行。

5.3 图片加载不出来但接口有数据

现象:商品列表能显示名称和价格,但图片位置是空白或者占位图。

原因:图片 URL 是 HTTP 明文地址,Android 9 以上默认禁止明文流量。

解决:在AndroidManifest.xml的application标签加android:usesCleartextTraffic="true"。如果图片存在本地服务器,确保图片路径拼接正确,别出现双斜杠或者少斜杠。

5.4 订单创建成功但库存没扣

现象:下单接口返回成功,订单表里有记录,但商品库存没变。

原因:库存扣减的 SQL 条件没写对,或者事务没生效。

解决:检查reduceStock的 SQL 是不是UPDATE product SET stock = stock - #{qty} WHERE id = #{id} AND stock >= #{qty},确认@Transactional加在 Service 方法上而不是 Controller 上。另外 MyBatis 的update返回值是受影响行数,如果返回 0 说明条件没匹配上,要抛异常回滚。

5.5 Gradle 同步失败提示找不到依赖

现象:Android Studio 打开项目后 Gradle 同步一直失败,提示Could not find com.android.tools.build:gradle:x.x.x。

原因:Gradle 版本和 Android Gradle Plugin 版本不匹配,或者仓库地址被墙。

解决:在项目根目录的build.gradle里确认 AGP 版本和gradle-wrapper.properties里的 Gradle 版本对应。国内网络可以在settings.gradle里把google()和mavenCentral()换成国内镜像源。改完后点File → Sync Project with Gradle Files重新同步。

6. 答辩前的自测清单与一个压箱底的调试习惯

答辩前一周,我一般会按下面这张表把整个流程走一遍,确保演示时不出岔子:

检查项操作预期结果
后端启动mvn spring-boot:run控制台无报错,端口 8080 监听
数据库连接访问/api/product/list返回 JSON 数组,字段完整
模拟器网络客户端首页下拉刷新商品列表正常显示,图片加载成功
登录流程输入测试账号密码跳转首页,Token 存入本地
下单流程加购后点结算订单表新增记录,库存减少
异常处理断网后点刷新提示「网络异常」而不是闪退

这张表看着简单,但每年都有同学在「断网测试」这一项翻车——演示时 WiFi 突然断了,App 直接崩溃,答辩老师脸色就不好看了。所以网络请求的回调里一定要判断response.isSuccessful()和response.body()是否为 null,别直接response.body().getData()。

最后说一个我自己的习惯:在客户端加一个隐藏的调试入口,连续点击版本号五次弹出一个对话框,里面显示当前 BaseUrl、Token 状态和最近一条请求的响应码。答辩演示时如果接口突然不通,点开看一眼就知道是地址错了还是 Token 过期了,比翻 Logcat 快得多。这个入口在正式打包时用BuildConfig.DEBUG判断一下,release 包自动隐藏。

做毕设最怕的不是功能少,而是演示时跑不起来。把上面这些点过一遍,至少能保证你站在讲台上时,App 是听你话的。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 1:18:16

TXS0102电平转换芯片翻车实录:三大常见错误与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:17:31

Type-C线缆的“智商”藏在哪里?CC逻辑与E-MARK芯片深度拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:16:39

BFD网络技术详解:原理、配置与路由联动实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:16:38

ESP32-S3开发板硬件设计深度解析:供电、引脚与USB OTG

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:16:38

Xilinx SelectIO IP核配置详解:从架构原理到上板调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:16:04

LM317/LM337双路可调稳压电源Multisim仿真详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华