ARTICLE DETAIL

资讯详情

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

Android Studio快递代拿App开发实战:从订单状态机到乐观锁并发方案

Android Studio快递代拿App开发实战:从订单状态机到乐观锁并发方案 简介面向毕业设计移动端的校园快递代拿跑腿App源码案例压缩包内为完整可参考工程适合计算机相关专业学生用于毕设选题或课程实践。包体共52个文件整体约540KB主体由20个JS脚本和19个Vue组件构成同时包含CSS样式、HTML入口、MySQL数据库脚本及package等配置并附有3张预览图轻量紧凑、目录清晰。项目围绕校园快递代取核心场景前端覆盖登录、下单、订单状态查询等流程涉及组件化页面、路由配置、网络请求封装数据库端通过SQL脚本设计用户表、订单表、快递信息表可实现基础业务关联。目前已有116人浏览学习。通过这份源码可梳理前端工程目录结构与交互逻辑对照数据库脚本理解数据表关系并在此基础上扩展跑腿接单、配送员管理、消息推送等功能为独立完成同类选题提供良好起点。1. 从毕业设计答辩现场说起这个跑腿App到底要做多少事校门口快递驿站堆成山代拿跑腿的需求在每个大学都存在这个标题让你用Android Studio把它做成App。基于这个标题毕业的源码案例本质上是一个小型C2C交易系统学生发布代拿订单跑腿员抢单配送管理员在后台看数据。很多人拿到这个题目就急着写界面其实答辩评委真正想听的不是你有几个Activity而是你有没有把订单状态流转、角色权限和并发冲突这些业务逻辑讲明白。这篇文章从架构设计、数据库建模讲到真机联调把每个环节该设的参数和该踩的坑都排一遍照着走完你手里会是一个能在两台手机上同时演示的完整项目。2. 整体架构与业务建模先把订单状态机画出来再写代码做之前花一天把架构定下来代码反而走得快。很多拿到这个项目的同学想在手机上用SQLite存所有订单客户端直连数据库直接读写这样写出的东西答辩时一问“两个用户怎么共享同一单数据”就卡住了。一个能跑起来的快递代拿App至少需要客户端、服务端和数据库三层客户端只负责展示和输入业务规则放在服务端数据统一落到MySQL里。2.1 客户端-服务端-数据库三层为什么毕业设计不能只写单机Demo客户端和服务端的分工要明确。Android端做的事登录、发单、浏览订单列表、抢单、更新订单状态、查看个人中心服务端负责用户认证、订单合法性校验、状态流转控制、数据统计。为什么不能把业务规则写在Android端因为客户端可以被绕过。比如发单时只在界面上校验“余额大于0”攻击者直接调用接口就可以带着负数金额下单服务端同样要校验一遍。常见做法是服务端返回JSON数据Android端用OkHttp或Retrofit发起请求数据校验在服务端做。这个项目的技术栈选型一般是开发工具Android Studio后端用Spring Boot或Servlet/JSP数据库用MySQL。如果你的毕设要求不高后端也可以用PHP写但Spring Boot生态更完整答辩时也更好讲。下面这张对比表能帮你确定该走哪条路对比项客户端直连数据库客户端-服务端-数据库实现速度两三天能跑通但后期返工概率大前期多一个服务端工程中期反而省事演示效果单台模拟器自导自演两个账号一台手机就能演示抢单全流程答辩风险被问“数据存在哪”容易露馅架构完整逻辑自洽代码量少但跨端协作没法演示稍重但每个模块边界清晰带学生做毕业设计时遇到选直连数据库的十个有九个在中期改回服务端方案。所以别省这一步直接把三层架构立起来后面每个功能点都有清晰的落点。2.2 订单状态机与角色权限代拿业务的核心是“状态流转”快递代拿的业务可以简化成六个状态已发布(0)、已接单(1)、取件中(2)、配送中(3)、已完成(4)、已取消(5)。用户发布订单后状态是0跑腿员抢单成功变1跑腿员到达驿站开始取件变2取完件往宿舍楼走变3双方确认收货变4只有状态是0和1时发布者可以取消订单。每次状态更新时服务端要校验“操作者是不是这个订单的合法角色”不能用户随便传个status4就把订单改了。角色权限分三类普通用户能发单、取消自己的订单限0和1状态、确认收货跑腿员能抢状态为0的订单、更新配送状态1→2→3、在后台看自己的接单历史管理员能查看所有订单、处理投诉、禁用违规账号。users表里的role字段区分0是普通用户1是跑腿员2是管理员。抢单是并发冲突最严重的环节第4章会专门讲乐观锁解法这里先记住一个原则任何状态变更都必须是服务端校验通过后的原子操作不能在客户端拼字符串传状态。2.3 技术栈选型Java还是Kotlin、SQLite还是MySQL、网络库怎么选这个标题写的是Android Studio但具体的开发语言在源码里不能含糊。课程里教的多是Java熟悉度高的就选Java如果你愿意学Kotlin它的协程写网络请求比回调更顺手。我一般建议Java因为你和答辩评委都熟悉代码示例多遇到问题搜起来快。网络库用Retrofit OkHttpRetrofit把JSON解析和接口定义绑在一起体感清爽不想用框架也可以只上OkHttp但Retrofit在代码组织上更有条理。数据层要分清两边服务端必须用MySQL或SQLite先顶研发环境上线前换MySQLAndroid端本地可以用SQLite做缓存和离线存储但不能拿它当主数据库。图片加载用Glide自带内存缓存和磁盘缓存省去手写图片压缩的麻烦。服务端框架如果自选Spring Boot和SSM二选一就够了不需要上微服务那套那是给自己找麻烦。还有一个细节Android Studio本身的工程配置gradle-wrapper.properties里的distributionUrl对应特定Gradle版本JDK版本也要匹配不然打开别人的源码工程会一直被版本问题卡住这个放到第5章避坑里细说。2.4 数据库表设计订单表、用户表、接单记录表的核心字段与建表SQL数据库是整个项目的底盘表结构设计不好后面每写一个查询都是坑。我一般会建三张核心表用户表users、订单表orders、接单记录表take_logs。用户表存账号、密码散列、昵称、手机号、角色、余额订单表是业务核心存发布者、接单者、取件地址、送达地址、跑腿费、状态、时间戳接单记录表记录每一次接单行为答辩时用来展示数据统计。订单表的建表SQL如下字段备注直接写清楚方便答辩时照着讲CREATE TABLE orders ( id INT NOT NULL AUTO_INCREMENT COMMENT 订单主键, order_no VARCHAR(32) NOT NULL COMMENT 订单编号时间戳随机数对外展示用, user_id INT NOT NULL COMMENT 发布者ID关联users表, take_user_id INT DEFAULT NULL COMMENT 接单者ID抢单成功后写入, pickup_address VARCHAR(64) NOT NULL COMMENT 取件地址如菜鸟驿站2号柜, delivery_address VARCHAR(64) NOT NULL COMMENT 送达地址如梅园3栋520, fee DECIMAL(5,2) NOT NULL COMMENT 跑腿费保留两位小数, status TINYINT NOT NULL DEFAULT 0 COMMENT 0已发布 1已接单 2取件中 3配送中 4已完成 5已取消, remark VARCHAR(255) DEFAULT NULL COMMENT 备注如快递单号, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 下单时间, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 最后操作时间, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_status (user_id, status), KEY idx_take_status (take_user_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT快递代拿订单表;字段类型和索引有几个参数需要说明。跑腿费用DECIMAL(5,2)而不用FLOAT是因为二进制浮点数算钱会有精度误差答辩时你主动提这一点评委印象分会上去。status用TINYINT而不是VARCHAR是为了状态更新语句里可以数字比较配合乐观锁更干净。订单编号用唯一索引因为现实中用户会拿order_no去驿站找件如果撞号就乱套了。联合索引idx_user_status和idx_take_status分别服务“我的发布列表”和“我的接单列表”两个高频查询这两个查询都有“当前用户ID 状态”的条件组合索引刚好命中。用户表和接单记录表的建表不贴完整代码了核心字段是users表要有role、phone、avatartake_logs表要有order_id、take_user_id、take_time给order_id建普通索引就行。表建好后记得在服务端启动时用一条初始化SQL插入一个管理员账号否则后台管理入口永远登不进去。3. 用Android Studio把用户端跑起来登录、发单与订单列表的实现架构和表结构定了就可以进Android Studio码代码。这一章从工程结构、登录注册到发单和列表刷新把用户端的主干功能串一遍。你拿到源码案例时先别急着逐个文件看先看工程结构是否清晰再看网络层封装是否统一最后看业务代码里有没有把状态校验写在客户端。Android Studio里常切一下目录视图很多新手在默认的Android视图下找不到values文件夹切到Project视图就能看到完整目录这个跟IDE版本没关系纯视图切换问题。3.1 项目结构划分按MVC分层避免一个Activity塞两千行拿到项目的源码工程第一件事先看包结构。一个规范的项目应该这样分activity包放页面LoginActivity、RegisterActivity、PublishOrderActivity、OrderListActivity、OrderDetailActivity、PersonalActivityadapter包放列表适配器OrderAdapter、CommentAdapterentity包放实体类User、Order、ApiResultnetwork包放网络请求封装RetrofitClient、ApiService、OkHttpInterceptorutils包放工具类MD5Util、SharedPreferencesUtil、ToastUtil。这样分层的好处是改动页面的逻辑只在activity调整数据展示只在adapter接口变动只在network每块代码都能单独替换。我见过一个OrderListActivity写了1500行的项目列表适配器、网络请求、图片加载全塞进去改一个字段要全局搜就是给自己埋雷。Android Studio里右键新包名把类拖进去就可以包名全部小写别用中文。这块没有太多技术含量但答辩时评委第一眼看的就是结构混乱的包名和到处放的全是灰的类印象分直接掉一档。3.2 登录注册模块MD5加盐存储与Token校验登录注册是最常被答辩追问的模块。常见做法是密码不存明文服务端存MD5加盐后的散列值。先看一个纯Java的MD5工具类这个放在utils包里public class MD5Util { // 盐值固定存储在服务端配置里不写死在客户端代码中 public static String md5WithSalt(String input, String salt) { String afterSalt input salt; try { MessageDigest md MessageDigest.getInstance(MD5); byte[] bytes md.digest(afterSalt.getBytes(UTF-8)); StringBuilder sb new StringBuilder(); for (byte b : bytes) { // 转成两位十六进制不足两位补0 sb.append(String.format(%02x, b)); } return sb.toString(); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { e.printStackTrace(); // 工具类里别返回null给个兜底值方便排查 return ; } } }这里加盐和不加盐的区别直接用MD5(“123456”)会被彩虹表秒破几乎所有常见密码的MD5都有预计算结果加盐之后就是MD5(“123456” 盐值)原始密码完全相同的两个人散列值也不同。注意盐不要写在客户端代码里有人把盐值常量写在Android的BuildConfig里反编译一下就暴露了服务端读取配置更稳妥。登录请求用Retrofit发POST。客户端把用户名和MD5加盐后的密文传给服务端服务端校验通过后返回一个Token客户端把Token和用户名存进SharedPreferences后续每次请求都在Header里带Token。这套流程在毕业设计里够用但你自己要清楚真实App不会只用MD5至少要走HTTPS加TLS不过教材项目里MD5加盐演示能自洽答辩不会在这题上深挖。3.3 发单页从选择快递点到提交订单的完整链路发单页是用户端最核心的业务页面它把前端表单和服务端数据串起来。一个完整的发单流程包括选择取件驿站、填写送达地址和备注、显示平台建议跑腿费、提交订单。跑腿费可以做一个滑动条最低1元、最高20元方便演示时快速调整价格。订单提交的核心逻辑是把表单数据封装成Order对象调用服务端发布接口。用Retrofit写起来是这样的public void submitOrder(Order order) { CallApiResultOrder call apiService.publishOrder(order); call.enqueue(new CallbackApiResultOrder() { Override public void onResponse(CallApiResultOrder call, ResponseApiResultOrder response) { // 服务端会二次校验防止绕过客户端直接提交非法数据 if (response.body() ! null response.body().getCode() 0) { ToastUtil.show(发布成功等待跑腿员接单); finish(); } else { ToastUtil.show(发布失败 response.body().getMessage()); } } Override public void onFailure(CallApiResultOrder call, Throwable t) { // 网络异常时保留用户填的内容不要直接清空表单 ToastUtil.show(网络异常请检查连接); } }); }注意一个细节onFailure里不直接清空表单因为很多情况下只是服务端暂时没响应用户只需要重试提交重新填写很影响体验。参数方面Order对象要在entity包里建好字段名和服务端JSON字段保持一致Gson才能直接反序列化。跑腿费是BigDecimal类型中文取值、驿站名都涉及编码问题Android Studio默认是UTF-8别改成GBK否则换电脑后中文注释全变乱码。3.4 订单列表刷新轮询间隔怎么设才不费电订单列表的刷新方式是这个项目躲不开的设计决策。常见做法有两种轮询和长连接。毕业设计场景里我选轮询因为WebSocket和MQTT要让服务端配套改造工作量涨一倍而订单数据本来就不是强实时晚几秒看到新单没有影响。轮询用Handler加Runnable实现在订单列表页的onResume里启动。间隔我一般设15秒兼顾及时性和电量。太短的话比如3秒一页打开一晚上要请求几千次服务器扛不住手机会持续唤醒网络模块太长的话60秒演示时评委等你刷新等得无聊。private final Handler refreshHandler new Handler(Looper.getMainLooper()); private final Runnable refreshRunnable new Runnable() { Override public void run() { loadOrderList(); // 拉取最新数据并更新Adapter refreshHandler.postDelayed(this, 15000); // 15秒后再次执行 } }; Override protected void onResume() { super.onResume(); loadOrderList(); // 从后台切回来时立即刷一次不等待轮询 refreshHandler.postDelayed(refreshRunnable, 15000); } Override protected void onPause() { super.onPause(); refreshHandler.removeCallbacks(refreshRunnable); // 页面不可见时停掉轮询 }这个写法有两个关键点。第一onResume里先手动刷一次再启动轮询用户从详情页返回列表时能立刻看到订单状态更新不用干等15秒。第二onPause里必须removeCallbacks否则页面跳到详情页后轮询还在后台跑白白浪费流量还会在返回时触发重复数据闪烁这就是典型的低续航和页面卡顿翻车现场。哪怕轮询间隔设好了这两行代码漏掉前面全白做。4. 接单端与后台管理跑腿小哥怎么抢单、管理员怎么审核用户端能发单跑腿员端能抢单这才算一个完整的闭环。接单端是快递代拿项目里最容易把实现细节讲出彩的地方原因是这个场景天然有并发问题两个跑腿员在同一秒看到同一单谁先抢到这个问题的解法写清楚答辩基本稳了。4.1 抢单接口的设计用状态字段做乐观锁防止两人同时抢到抢单操作从用户视角是“点一下按钮”从服务端看是一次高风险的并发更新。最简单的错误写法是先查订单状态如果是已发布就把它改成已接单。高并发下两个请求同时查到已发布都会执行更新结果就是同一单被两个跑腿员接走这就是一次典型的并发竞态。正确的做法是乐观锁把状态判断下沉到UPDATE语句里。下面是服务端Mapper里的核心SQLUPDATE orders SET take_user_id #{takeUserId}, status 1, update_time NOW() WHERE id #{orderId} AND status 0 AND take_user_id IS NULL这段SQL的关键在WHERE条件里不仅要求当前状态是0还要求接单者ID为空双重校验。执行后看影响行数返回1说明抢单成功返回0说明订单已经被别人抢了直接提示“手慢了”。乐观锁的意思就是这样不加数据库层面的锁而是靠条件WHERE把更新限制在安全范围内谁先执行就锁住状态后到的人自然更新0行。对应的Java接口处理逻辑要加一个事务注解抢单失败时抛业务异常回滚Transactional public boolean grabOrder(Long orderId, Long takeUserId) { // 先做一次角色校验非跑腿员角色直接拒绝 User user userMapper.selectById(takeUserId); if (user null || user.getRole() ! 1) { throw new BusinessException(当前账号没有接单权限); } int rows orderMapper.grabByStatus(orderId, takeUserId); return rows 0; }用Transactional是必须的因为抢单成功后可能还要往take_logs表插记录这两个写入动作任何一个失败整体都要回滚。抢单成功后顺手插一条接单记录后台统计接单量时有数据可查。这套逻辑里最关键的是SQL语句里的AND status 0它把“检查”和“更新”合并成一个原子操作避免了检查后更新前别人插入的窗口期。4.2 我的接单列表RecyclerView多布局与订单状态切换跑腿员端的“我的接单”和用户端的“我的发布”只是数据源相同、操作按钮不同写的时候用RecyclerView多布局区分。getItemViewType根据订单status返回不同布局类型Adapter里给不同状态展示不同按钮状态1显示“取件”状态2显示“开始配送”状态3显示“确认送达”。用户端看到已接单就只显示“等待配送中”不能出现取件按钮。按钮点击事件放在Activity里处理用接口回调从Adapter传出来职责清晰。我见过直接把业务代码写在ViewHolder里的改起来非常痛苦按钮逻辑要复制好几份。正确的姿势是Adapter只管视图绑定点击事件统一走一个OnOrderActionListener接口Activity里根据订单ID和操作类型调对应的服务端接口。这里涉及一个细节RecyclerView的条目复用会导致按钮点击错乱所以每个按钮的点击监听里都要拿着holder.getBindingAdapterPosition()去取当前条目的订单数据不能直接用position变量这个坑在快速滑动时特别明显。4.3 后台数据看板统计SQL与图表选型后台管理页面通常不在Android端做但毕业设计里很多人把它并进同一个App管理员登录后看到的就是一个统计页面。这个页面需要展示的数据一般有四块今日新增订单、进行中的订单数量、累计注册用户、待处理投诉。用三到四条聚合SQL就能算出来-- 今日新增订单数 SELECT COUNT(*) FROM orders WHERE DATE(create_time) CURDATE(); -- 进行中订单数已接单到配送中这3个状态 SELECT COUNT(*) FROM orders WHERE status IN (1, 2, 3); -- 注册用户总数 SELECT COUNT(*) FROM users; -- 各状态订单分布喂给饼图用 SELECT status, COUNT(*) AS cnt FROM orders GROUP BY status;第一段的聚合SQL返回值是单行单列服务端直接包成Map返回即可。调试时如果发现统计数字对不上先单独在Navicat里跑一遍SQL确认是不是联表查询出了问题再去看Java代码。图表展示我一般用MPAndroidChart饼图展示订单状态分布折线图展示最近七天订单趋势。折线图要按日期分组SQL写DATE(create_time) BETWEEN ... GROUP BY DATE(create_time)查出来是个List端上转成Entry数组。图表库记得在build.gradle里引依赖ProGuard混淆时必须keep住否则发布版直接白屏这就是为什么发布前必须测一次release包的原因。4.4 服务端接口约定统一返回格式与错误码不论客户端还是后台所有接口都返回同一个JSON包装结构这是新手最容易忽略但最能体现工程规范的点。建议的格式是code为int型0表示成功非0表示业务错误码message为给用户看的提示语data为泛型数据可以是对象、列表或者空。对应的错误码要有对照表1001参数缺失、1002账号不存在、1003密码错误、1004账号被禁用、2001订单不存在、2002订单已被抢、2003无权操作此订单。把这些写在ApiResult实体类里服务端和Android端共用一套语义。Android Studio里创建values文件夹时多语言这种字符串资源可以放string.xml但业务提示建议直接走message字段服务端改文案不用重新发版对毕业设计来说性价比更高。统一返回格式还有一个好处Retrofit的解析逻辑只需要写一次错误判断全部集中在onResponse的code分支里不会出现每个接口各写一套异常处理的混乱局面。5. 真机联调与打包发布连不上服务器、闪退、OOM的排查避坑指南这一章没有高深的原理全是实操中会堵住你的真实坑点。每一步都标注了现象、原因和解决都是这个项目里最常见的翻车点。遇到问题别盯着代码一行行看先按下面的清单对一遍大部分都能直接定位。5.1 模拟器访问本机后端10.0.2.2还是局域网IP现象Android Studio模拟器里App请求http://localhost:8080/接口永远连不上控制台报Connection refused。原因模拟器里的localhost指向的是模拟器自己而不是你的开发电脑所以需要访问宿主机的特殊地址10.0.2.2。解决开发环境把Retrofit的baseUrl写成http://10.0.2.2:8080/真机调试时改成电脑的局域网IP比如http://192.168.1.100:8080/。如果换了网络环境IP要记得更新。我一般在Constants类里留一个isEmulator开关跑模拟器时自动切到10.0.2.2。这里有一个容易忽略的问题真机调试时手机和电脑必须在同一WiFi且要关掉电脑防火墙对Java进程的限制否则服务端接口在浏览器里能打开手机上死活连不上玄学一样的现象背后就是防火墙规则。5.2 Android 9默认禁用HTTP明文流量网络安全配置怎么加现象请求明明没写错但抛出的异常是Cleartext HTTP traffic to xxx not permitted。原因从Android 9API 28开始系统默认禁止应用使用HTTP明文协议而毕业设计里服务端通常没配HTTPS证书所以全被拦截。解决在AndroidManifest.xml的application标签里加一行android:usesCleartextTraffictrue或者用网络安全配置只对调试域名放行。完整配置是这样application android:labelstring/app_name android:usesCleartextTraffictrue ... /application这个开关在正式发布项目时应该分环境处理Debug包开着方便联调Release包关掉走HTTPS。但毕业设计没有Release上线压力直接开着最省事。注意如果把这个属性写在某个Activity上那是无效的必须写在application标签上。这个属性是API 23才有的Android Studio里的minSdkVersion别设太低不然低版本设备上直接编译报错。5.3 图片加载OOM缩略图尺寸与本地缓存现象订单列表里带了几张快递驿站照片滑动几下应用就闪退了Logcat里是OutOfMemoryError。原因直接加载原图会让Bitmap占满堆内存一张3000×4000的照片解码后大约占用45MB内存滑动加载十几张就爆了。解决使用Glide替代手动加载它会按控件尺寸自动采样Glide.with(context) .load(imageUrl) .override(400, 400) // 强制解码400x400的缩略图 .centerCrop() .into(imageView);override参数是解决OOM的关键Glide内部会先读图片宽高做采样计算不会一次性解原图。磁盘缓存策略也可以打开用skipMemoryCache(false)和diskCacheStrategy(DiskCacheStrategy.RESOURCE)配合减少重复网络请求。图片不是这个App的主要数据但OOM问题一旦出现排查起来比业务代码复杂得多所以先在前端拦一道。另外一个隐藏坑Glide的图片加载在ListView或RecyclerView快速滑动时会有大量请求如果服务端图片服务器带宽不够图片加载会超时这时候要在Glide的RequestOptions里设置timeout别用默认的无限超时。5.4 轮询通知被系统静默杀死前台服务与WorkManager现象手机锁屏后跑腿员的轮询请求不再执行亮屏解锁后又恢复正常以为是自己代码有问题。原因从Android 6.0开始引入Doze模式屏幕熄灭一段时间后系统会把后台网络请求挂起这是系统级别的限制跟代码逻辑无关。解决把订单监听改成前台服务在通知栏挂常驻通知这样系统视为用户可见任务不会休眠。或者用WorkManager做周期任务但WorkManager周期任务的最小间隔是15分钟对实时接单场景不够用所以我推荐前台服务方案。前台服务在onStartCommand里startForeground创建时传一个通知ChannelAndroid 8.0以上必须有Channel否则通知不显示。这个方案演示时有个小副作用通知栏会一直有个“订单监听中”的提示反而能成为答辩时展示你对Android系统机制理解的一个细节。注意前台服务启动后要在onDestroy里stopForeground和stopSelf否则服务一直挂着在设置界面看耗电排行会非常难看。5.5 发布前的签名与混淆哪些类在ProGuard里不能混淆现象Debug包跑得好好的打正式包签名后用另一台手机安装一启动就闪退或者登录后返回数据解析失败。原因正式包默认开启代码混淆Gson解析需要访问实体类的字段名混淆后字段名变成a、b、cJSON映射就全乱了。解决在proguard-rules.pro里保留实体类、网络请求接口和自定义注解不能混淆。常用规则是-keep class com.example.entity.** { *; } -keep class com.example.network.** { *; } -keep class com.google.gson.** { *; }发布前记得做一次全流程回归测试用正式签名包跑一遍登录、发单、接单、确认收货。混淆是最容易在发布前一刻翻车的环节别等答辩现场装正式包才发现白屏。Android Studio打不开别人的源码工程通常是gradle版本和JDK对不上打开后先看gradle-wrapper.properties里的distributionUrl缺什么版本就让它自动下载IDE卡死把gradle jvm内存调大一点再重开。6. 答辩演示脚本与验证清单让评委相信你真正做过答辩时不要从头点一遍页面评委想看到的是你对业务的理解。按“异常优先”的顺序演示先展示服务端的报错返回再演示正常流程最后用两个账号制造一次冲突。先用一个还没登录的账号点“发单”演示客户端校验和401拦截然后正常登录用户端发一单跑腿员端抢单展示订单状态从0变成1同时注意观察服务端控制台打印的SQL日志确认乐观锁的WHERE条件确实生效。接着再开一个跑腿员账号抢同一单弹出“手慢了”这就是给评委看并发处理的真实数据比口说百句都强。验证清单上建议列这几个场景同一账号重复抢同一单、越权把订单状态改成4、管理员看板数据与订单表实际记录是否一致、两个账号同时抢单时最终只产生一条接单记录。前三项在演示时每个都值得停下来讲一句背后的判断逻辑第四项是最有含金量的因为很多毕业生都栽在这里。可以把orders表的status字段手动改成一个非法值比如99然后调用接口服务端应该返回2003无权操作这个异常分支我也有一次忘了处理答辩被评委问了一下才意识到状态枚举校验没做完。最后给自己留一条后路提前把SQL日志打印打开真到演示翻车时就指着日志说“看这里校验失败了”比黑匣子强得多。整个项目做下来你会发现自己能讲清楚的不只是几个页面而是订单状态机、乐观锁、Doze模式、混淆配置这些真正值钱的问题。当年我做类似题目时走了客户端直连数据库的弯路后来改服务端接口才把逻辑理顺现在把这个习惯留给你。希望帮到你。本文还有配套的精品资源点击获取
返回列表