☰
基于Android的物流管理系统:Java服务端与前后端交互实战解析
2026/9/26 10:41:27 网站建设 项目流程

简介:基于Android的物流管理系统完整项目,服务端采用Java Web技术栈,整合JSP、Servlet、Ajax异步交互与MySQL数据库,适合正在做课程设计、毕业设计或想学习Android与Java服务端融合开发的读者。项目按表现层、业务层、数据访问层分层,业务模块均设计有专门接口与实现类,代码结构清晰,便于二次开发。压缩包共899个文件,大小约9.48MB,主要包含47个Java源码及对应class文件、32个JSP页面、116个JS脚本、24个CSS样式,以及大量界面切图与开源依赖库,覆盖前端页面、后端逻辑、数据库配置等完整环节。已有505人学习下载。通过这份项目可掌握Android客户端与Servlet服务端通过Ajax交互的流程,理解MVC结构下的页面组织与业务封装方式,并获取一套可运行、可改造的物流管理解决方案,对提升企业级项目分层设计能力很有帮助,同时其中包含的新闻公告、报单管理等业务模块也可作为功能扩展与二次开发的基础。

1. 基于 Android 的物流管理系统:Java 服务端代码到底能干什么

做过课程设计的人应该都有体会,Android 单页能写,Java 后端也能写,但把两边的数据链路跑通才是最花时间的。这份基于 Android 的物流管理系统,服务端用 Java 实现,正好把这条链路完整串起来:App 端登录、查订单、更新配送状态,服务端接收 HTTP+JSON 请求并操作数据库,再回传结果。适合正在整 JavaWeb 毕业设计、Android 课程设计,或者想找一套完整前后端交互源码的读者。下面按客户端、服务端、数据库、联调排查、线上验证这个顺序拆开讲。

2. Android 客户端:从登录页到订单列表的网络交互

2.1 拿到工程后先看包结构,再确认三个实体类

我拿到这种资源包,不会急着点运行,而是先看 Android 工程里是不是同时包含了 activity、entity、util 这几层。很多课程设计源码的界面写得很乱,但核心逻辑其实集中在两个地方:entity 里的订单实体和 util 里的请求工具类。先把这两个目录看懂,后面改界面、换接口都顺手。

实体类对应的字段通常和数据库表一一对应。一般会有 UserInfo、OrderInfo、TrackInfo 三个类,其中 OrderInfo 是最关键的,因为物流订单的增删改查都围绕它。老一点的工程里字段都写成 private int id,再给 getter/setter,新一点的可能用 kotlin data class,但不管哪种形式,字段名一定要和服务端返回的 JSON key 保持一致,这是 Android 端最容易翻车的地方。字段对不齐,JSONObject 解析时直接拿不到值,页面就空白。

下面是一份典型的 OrderInfo 实体,对应物流订单表:

public class OrderInfo { private int id; private String orderNo; private String goodsName; private String fromAddress; private String toAddress; private int status; private int driverId; public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } // 其他 getter/setter 省略 }

这个类里的 status 是物流状态码,0 代表待接单、1 代表运输中、2 代表已签收。我一般建议把状态判断放在 Android 端做,而不是等服务端把“运输中”三个字拼好传过来。原因很简单:前端可以随时改文案,后端只负责给数字,接口职责更清晰。

2.2 把 HttpURLConnection 封装成 HTTP+JSON 请求工具类

Android 和服务端交互,最稳定的做法是用 HttpURLConnection。它比 HttpClient 少引一个第三方库,兼容性也够。资源包里如果是旧的 Apache HttpClient 写法,在 Android 6.0 之后维护成本很高,我建议拿到手后统一替换成 HttpURLConnection 封装。

我一般会写一个 HttpUtil,把 POST JSON 请求抽出来,所有业务页面共用。代码如下:

public class HttpUtil { private static final int CONNECT_TIMEOUT = 5000; private static final int READ_TIMEOUT = 5000; public static String postJson(String urlPath, String json) throws IOException { URL url = new URL(urlPath); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setReadTimeout(READ_TIMEOUT); conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); os.write(json.getBytes("UTF-8")); os.flush(); os.close(); if (conn.getResponseCode() == 200) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } return sb.toString(); } return null; } }

这段代码里两个超时时间很容易忽略。connectTimeout 是建立连接的时间限制,readTimeout 是拿到响应数据的等待时间。如果服务端某个接口查询很慢,只设置 connectTimeout 没用,readTimeout 超了照样会崩。实际项目里我会把这两个值分开配,连接超时短一点、读取超时长一点,避免用户等太久。

另外注意charset=UTF-8必须同时出现在请求头和请求体里,否则服务端用request.getReader()读出中文时可能已经乱码。因为 Android 端写出去的 JSON 是 UTF-8 字节,请求头不声明的话,Tomcat 默认按 ISO-8859-1 解码,中文会变成问号。

2.3 登录逻辑:子线程请求与回包解析

Android 主线程不能做网络请求,这是多少年的死规矩。拿到源码后第一件事就是检查登录按钮的 onClick 里有没有直接 call HttpUtil,如果有,说明这份代码在旧模拟器上能跑,换个新环境就会抛 NetworkOnMainThreadException。

安全的做法是新建 Thread 执行请求,再通过 runOnUiThread 切回主线程更新界面。下面这段登录逻辑基本可以直接套用:

private void handleLogin(final String username, final String password) { final String url = Constant.BASE_URL + "/api/login"; new Thread(new Runnable() { @Override public void run() { try { JSONObject json = new JSONObject(); json.put("username", username); json.put("password", password); String result = HttpUtil.postJson(url, json.toString()); if (result == null) { return; } final JSONObject response = new JSONObject(result); runOnUiThread(new Runnable() { @Override public void run() { if (response.optInt("code") == 200) { startActivity(new Intent(MainActivity.this, OrderListActivity.class)); } else { Toast.makeText(MainActivity.this, response.optString("msg"), Toast.LENGTH_SHORT).show(); } } }); } catch (Exception e) { e.printStackTrace(); } } }).start(); }

这里有个细节:result == null要单独判断,因为 HttpUtil 里非 200 返回 null。如果不判空,下一行new JSONObject(result)会直接抛 null 异常。很多新手在这地方踩坑,以为是网络问题,其实是自己的空指针问题。

登录接口的 code 字段要和服务端约定好,常见的是 200 成功、401 失败。实际项目里我会让服务端多返回一个 token 字段,客户端保存到 SharedPreferences,后面的订单接口再带上这个 token。但课程设计源码为了演示方便,通常会省略鉴权,直接传 userId,这部分可以按需改造。

2.4 订单列表请求:JSONArray 转 List

订单列表页是所有物流系统的主界面,也是 AJAX 交互最典型的一个场景。客户端请求服务端返回订单列表页需要的数据,服务端把 List 转成 JSONArray,再包一层 JSONObject 传回来。客户端这边用 AsyncTask 发请求最直观:

public class OrderListTask extends AsyncTask<Void, Void, List<OrderInfo>> { private Context context; public OrderListTask(Context context) { this.context = context; } @Override protected List<OrderInfo> doInBackground(Void... voids) { try { String result = HttpUtil.postJson(Constant.BASE_URL + "/api/order/list", "{}"); if (result == null) return new ArrayList<>(); JSONObject obj = new JSONObject(result); JSONArray array = obj.optJSONArray("data"); List<OrderInfo> list = new ArrayList<>(); for (int i = 0; i < array.length(); i++) { JSONObject item = array.getJSONObject(i); OrderInfo info = new OrderInfo(); info.setOrderNo(item.optString("orderNo")); info.setGoodsName(item.optString("goodsName")); info.setFromAddress(item.optString("fromAddress")); info.setToAddress(item.optString("toAddress")); info.setStatus(item.optInt("status")); list.add(info); } return list; } catch (Exception e) { e.printStackTrace(); return new ArrayList<>(); } } @Override protected void onPostExecute(List<OrderInfo> list) { if (!list.isEmpty()) { adapter.setData(list); adapter.notifyDataSetChanged(); } } }

这段代码的关键是 optString、optInt,而不是 getString、getInt。opt 前缀的方法在字段缺失时不会抛异常,会返回空字符串或 0。课程设计阶段接口字段经常调整,用 opt 系列方法可以避免一改字段就崩。虽然会隐藏一些问题,但对于演示系统来说够用了。

拿到 List 后,界面层再根据 status 显示成不同颜色。比如 status 为 0 的显示“待接单”橙色,1 显示“运输中”蓝色,2 显示“已签收”灰色。这个逻辑放客户端,将来状态规则变了只改一个文件,不需要动服务端。

3. Java 服务端:Servlet 接收请求,Dao 与数据库交互

3.1 服务端分层的约定与请求路径映射

打开资源包里的服务端目录,最明显的特征是没有 Spring,而是直接用 Servlet。这种方式在课程设计和一些小系统里很常见,因为部署简单、依赖少,把 war 包往 Tomcat webapps 一扔就能跑。我拿到这类代码后,会先看 web.xml 和类上的 @WebServlet,理清接口路径和类的对应关系。

一般会分成三层:Servlet 层负责接请求、Service 层负责业务判断、Dao 层负责 JDBC 数据库操作。有的源码为了省事会把 Service 层省略掉,直接在 Servlet 里写数据库查询,这就意味着逻辑和连接耦合在一起,性能差但更好理解。

一个典型的映射是这样:客户端请求http://ip:8080/项目名/api/login,Tomcat 根据 @WebServlet 注解找到对应的 LoginServlet,然后调用 doPost 处理。注意项目名一定不能漏,很多同学在浏览器里请求 localhost:8080/api/login 找不到接口,就是因为 Tomcat 下应用上下文是 /express,正确地址应该是 /express/api/login。

3.2 登录接口:从 doPost 到 JSON 响应

登录接口是这种管理系统的基础,服务端要做的三件事:读 JSON、查数据库、写回 JSON。下面这段代码基本能说明 Servlet 的完整流程:

@WebServlet("/api/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=UTF-8"); BufferedReader reader = request.getReader(); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } JSONObject reqJson = new JSONObject(sb.toString()); String username = reqJson.getString("username"); String password = reqJson.getString("password"); UserDao userDao = new UserDao(); User user = userDao.login(username, password); JSONObject result = new JSONObject(); if (user != null) { result.put("code", 200); result.put("msg", "登录成功"); JSONObject data = new JSONObject(); data.put("userId", user.getId()); data.put("username", user.getUsername()); result.put("data", data); } else { result.put("code", 401); result.put("msg", "用户名或密码错误"); } response.getWriter().write(result.toString()); } }

读者可能注意到这里没有调用 doGet,因为接口只接受 POST 请求。如果你从浏览器直接打开这个地址,Tomcat 会返回 405。这是前后端分离的常见约定:登录、提交订单这类操作都用 POST,因为参数在请求体里不暴露在 URL 上。

request.getReader()返回的是 BufferedReader,按行读取请求体里的 JSON 字符串。如果请求体为空,第二行new JSONObject(sb.toString())会抛异常。作为演示代码可以不处理,但真实项目里一定要加 try-catch,返回一个 code=400 的错误对象。我一般在资源包的基础上补一个 BaseServlet 的父类,统一处理异常和日志,增删改查类只需要实现业务代码。

3.3 JDBC 工具类与用户查询,密码校验要注意的事

服务端的数据访问层用 JDBC 连接 MySQL,是比较老的但能讲清原理的方案。资源包里通常有一份 DBUtil,我把它改成过私有静态常量,避免到处改:

public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/express_db?useUnicode=true&characterEncoding=utf-8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

characterEncoding=utf-8这行很重要,它保证从数据库读中文时不会乱码。如果不加,很多时候服务端返回的数据中文成了 ??,客户端解析后整个界面全是问号,排查起来非常迷惑。

用户查询建议用 PreparedStatement 而不是字符串拼接,防止 SQL 注入。写法如下:

public class UserDao { public User login(String username, String password) { String sql = "SELECT * FROM sys_user WHERE username = ? AND password = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setRole(rs.getInt("role")); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; } }

注意这套代码是明文密码校验。课程设计里大家都这么写,因为方便演示,但我不建议你在正式项目里照搬。如果能改造,推荐用 MD5 加盐或者 BCrypt 存密码,登录时先加密再比对。如果只是想跑通资源包,这个 Dao 层逻辑已经是完整的了。

4. 数据库设计:用户、订单、物流轨迹三张核心表

4.1 建表 SQL 与字段设计思路

物流管理系统的核心数据关系其实不复杂,主要就是用户 + 订单 + 订单轨迹。用户表对应登录账号,订单表存物流单,轨迹表存每个时间点的位置信息。把这三张表建好,剩下的统计报表都能通过关联查询查出来。

下面是一份可以直接导入 MySQL 的建表脚本:

CREATE DATABASE IF NOT EXISTS express_db DEFAULT CHARACTER SET utf8mb4; USE express_db; CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL, role TINYINT COMMENT '0-管理员 1-司机 2-客户', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT='用户表'; CREATE TABLE logistics_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) UNIQUE NOT NULL, goods_name VARCHAR(100) NOT NULL, from_address VARCHAR(200) NOT NULL, to_address VARCHAR(200) NOT NULL, status TINYINT DEFAULT 0 COMMENT '0-待接单 1-运输中 2-已签收', driver_id INT NULL, user_id INT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT='物流订单表'; CREATE TABLE logistics_track ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, location VARCHAR(200), remark VARCHAR(200), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT='物流轨迹表';

先说几个容易忽略的字段设计点。status 用 TINYINT 而不是 VARCHAR,是因为状态判断只需要一个数字做等值比较,存储和索引都比字符串高效。order_no 设置 UNIQUE,是为了防止并发时重复生成单号。update_time 用 ON UPDATE CURRENT_TIMESTAMP,这样订单每次更新状态时都会自动写入当前时间,不用在每一条 UPDATE SQL 里手动维护。

我没有建外键约束,这是故意的。课程设计源码经常要迁移数据库,外键会导致导入失败率高。只要在业务代码里保证 order_id 一定存在,逻辑外键就够用了。

4.2 订单列表关联查询与状态更新

订单列表接口的数据不只是订单表自身字段,还希望看到司机名字,这就需要 join 查询。一个订单可能还没有司机接单,所以要用 LEFT JOIN 而不是 INNER JOIN,否则没人接单的订单会从列表里消失。

下面这条 SQL 适合直接放在 OrderDao 里:

SELECT o.id, o.order_no, o.goods_name, o.from_address, o.to_address, o.status, u.username AS driver_name FROM logistics_order o LEFT JOIN sys_user u ON o.driver_id = u.id ORDER BY o.create_time DESC;

driver_name这个别名会在结果集的 JSON 里直接出现。Android 端拿到 JSON 后,可以直接取这个字段显示在列表上。注意如果司机不存在,driver_name 会是 null,客户端用 optString 的时候会变成空字符串,比直接显示“null”要体面得多。

状态更新是另一个高频操作。司机接单时执行:

UPDATE logistics_order SET status = 1, driver_id = ?, update_time = NOW() WHERE id = ?;

签收时执行:

UPDATE logistics_order SET status = 2, update_time = NOW() WHERE id = ?;

业务代码里要把这两段 SQL 放在 Service 层里做判断,比如只有“待接单”状态才更新成“运输中”。如果直接在 Servlet 里 UPDATE,状态可以从 2 跳回 1,逻辑就乱套了。

4.3 初始化数据,顺便回答两个常见设计问题

为了让工程第一次跑起来就有数据显示,数据库脚本里通常要带几条测试数据:

INSERT INTO sys_user (username, password, role) VALUES ('admin', '123456', 0), ('driver001', '123456', 1), ('customer001', '123456', 2); INSERT INTO logistics_order (order_no, goods_name, from_address, to_address, status, driver_id, user_id) VALUES ('LG20240001', '手机配件', '北京朝阳', '上海浦东', 1, 2, 3), ('LG20240002', '办公椅', '广州天河', '深圳南山', 0, NULL, 3);

这里导入了三个账号,登录时可以直接用。admin 是管理员,driver001 是司机,customer001 是客户。角色不同,登录后看到的页面不一样,但接口层往往不怎么区分权限,只在界面层隐藏按钮。

另一个常见问题是:为什么订单表和用户表要分开?因为订单的客户和司机都可能变化。如果把用户名复制到订单表里,改一次名字就要改所有历史订单,而且 join 查询也能保证业务上“司机是用户表里那个”的约束。这种设计对课程设计来说足够规范,答辩时能讲清楚也算加分项。

5. 联调避坑:环境、编码与权限的常见问题

5.1 联调前检查清单

跑这套项目最容易耗时间的不是写代码,而是环境对不上。现在模拟器和真机的网络规则都变了,直接打开旧源码很容易在各种隐藏问题上卡住。拿到资源包后,我建议先把下面这张表和你的环境比对一遍。

检查项推荐配置说明
Android 模拟器访问后端http://10.0.2.2:8080/express10.0.2.2 是模拟器访问宿主机的固定地址
Android 真机访问后端http://电脑局域网IP:8080/express手机和电脑必须在同一个 Wi-Fi
Tomcat 端口8080被占用时改成 8081,记得同时改客户端
MySQL 地址localhost:3306如果改端口,DBUtil 里的 URL 也要改
数据库编码utf8mb4建库时设置,建表自动继承
登录账号admin / 123456初始化脚本里自带

这张表的第一行是很多新人的痛。模拟器里输入的 localhost 指向模拟器自己,不指向电脑,所以必须写成 10.0.2.2。真机则正好相反,不能写 localhost,要写电脑在局域网里能被手机访问到的 IP,比如 192.168.1.8。这个 IP 可以在电脑的ipconfig命令里查到。

5.2 五条高频踩坑记录

坑 1:模拟器连接超时,Logcat 提示 Connection refused

原因:客户端 BASE_URL 写成了 http://localhost:8080,模拟器认为这是它自己。 解决:改成 http://10.0.2.2:8080/express。如果还是不行,检查 Windows 防火墙是否拦截了 Tomcat 的 8080 端口。

坑 2:服务端返回的中文变成问号或乱码

原因:三层编码不统一。Servlet 没有设置 request 和 response 的编码,或者 MySQL 连接 URL 缺少 characterEncoding=utf-8。 解决:在 doPost 开头加request.setCharacterEncoding("UTF-8")和response.setContentType("application/json;charset=UTF-8"),数据库 URL 带上?useUnicode=true&characterEncoding=utf-8。

坑 3:Android 运行就崩,报 NetworkOnMainThreadException

原因:项目的网络请求写在了 Activity 的 onCreate 或 onClick 方法里,运行在 UI 线程。 解决:把请求放进new Thread()或者 AsyncTask。资源包里的代码如果是老写法,这个坑基本必踩。

坑 4:Android 9 以上请求 http 报 Cleartext HTTP traffic not permitted

原因:Android 默认不允许明文 HTTP 请求,这类课程设计全是 http 接口。 解决:在 AndroidManifest.xml 的 application 节点加android:usesCleartextTraffic="true"。加了之后注意不要把 targetSdkVersion 改得过高,否则有些设备还是会拦截。

坑 5:Tomcat 启动报 ClassNotFoundException: com.mysql.jdbc.Driver

原因:MySQL 驱动 jar 包没有放在服务端的 WEB-INF/lib 目录下。 解决:把 mysql-connector-java 的 jar 包拷贝到 WEB-INF/lib,然后右键 Add as Library,重启 Tomcat。这个 jar 网上随便搜就有,但要和 MySQL 版本匹配。

5.3 用日志判断问题出在客户端还是服务端

联调时最怕两端各说各话,谁也说不清请求到底到没到服务端。我的排查顺序是先看服务端日志,再抓 Android 请求。Tomcat 的 console 里如果打印了doPost相关的日志,说明请求已经进入 Servlet;如果没有,说明网络层就断了,问题在客户端或环境。

Android 端抓请求时,我在 HttpUtil 的 postJson 方法里加过一行临时日志:

Log.d("HttpUtil", "url=" + urlPath + " body=" + json);

请求发出前打印 URL 和请求体,响应回来后打印 result。这能快速看出是请求没送到,还是服务端返回的数据不符合预期。课程设计源码里可能没有这个日志,你可以在自己的代码里补上,调试完再删。

还有一个玄学问题:模拟器能访问服务端,真机不能。这种多半是安卓手机把局域网权限拦了,或者电脑开启了“专用网络”之外的防火墙配置。我一般先在手机浏览器里打开服务端的登录接口地址,看看能不能访问,如果能,再查 App 代码;如果浏览器也打不开,问题就在 IP、端口或防火墙。

6. 进阶:用 curl 先验证服务端,再给订单加一个轨迹上报接口

6.1 curl 模拟 JSON 请求

调接口不一定非得打开 Android 模拟器。我先用 curl 把服务端的关键接口打一遍,确认后端可跑,再回客户端改代码。这样排查范围会缩小很多。

登录接口的验证命令是这样的:

curl -X POST http://localhost:8080/express/api/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}'

正常情况下会返回一段 JSON,类似{"code":200,"msg":"登录成功","data":{"userId":1,"username":"admin"}}。如果这里返回的是 404,说明接口路径不对;返回 500,说明 Servlet 或数据库有问题;返回正常 JSON,说明后端链路通了,接下来才轮到 Android 端排查。

我每次拿到新的课程设计源码,都会先写一个这样的 curl 脚本文件,把项目里所有接口列进去。跑一遍就知道哪些接口真的能用,哪些是摆设。

6.2 新增轨迹上报接口

有了上面的验证习惯,加新接口就很快。比如给物流系统加一个司机上报当前位置的功能,服务端新建一个 TrackServlet,客户端就能持续上报定位。代码不长:

@WebServlet("/api/reportTrack") public class TrackServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=UTF-8"); BufferedReader reader = request.getReader(); JSONObject body = new JSONObject(reader.readLine()); int orderId = body.getInt("orderId"); String location = body.getString("location"); String remark = body.optString("remark", ""); TrackDao trackDao = new TrackDao(); boolean inserted = trackDao.insert(orderId, location, remark); JSONObject result = new JSONObject(); result.put("code", inserted ? 200 : 500); result.put("msg", inserted ? "上报成功" : "上报失败"); response.getWriter().write(result.toString()); } }

这里我用了optString("remark", ""),第二个参数是默认值。因为定位上报时,备注可能为空,如果客户端不发这个字段,直接 getString 会抛 JSON 解析异常。这个默认值参数在很多场景都能用,值得记住。

对应的 Dao 层插入方法,只需要往 logistics_track 表写一条记录:

INSERT INTO logistics_track (order_id, location, remark) VALUES (?, ?, ?);

上报成功后再看订单状态,业务上可以让司机先上报轨迹,再更新订单状态为“运输中”。这两步按理说应该在一个事务里完成,但课程设计通常不强调事务。如果你要扩展,可以在 Service 层用Connection.setAutoCommit(false)把两条 SQL 包起来。

从那以后,我每次拿到新的项目资源,都会强制走一遍这个流程:先开数据库,再部署服务端,用 curl 把所有接口打一遍,最后才启动 Android 客户端。这样能把“环境问题”和“代码问题”彻底分开,省下大量联调时间。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询