一、功能需求
- 送货单绑定
解析送货单二维码,展示药品清单,动态绑定对应追溯码,实时更新扫码进度。 -
追溯码解析
支持扫描追溯码(小码、大码),并通过解析获取药品信息,包括名称、规格、生产批次、有效期等。
二、UI界面
UI界面代码如下,功能需求很简单,先扫订单的二维码,得到订单号和供应商并填入编辑框,同时药品信息出现在列表,然后扫追溯码,出现提示,最后提交入库:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F9F9F9"
android:padding="16dp"
tools:context=".MainActivity">
<!-- 标题 -->
<TextView
android:id="@+id/title_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:gravity="center"
android:text="送货单追溯管理"
android:textColor="#333333"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- 订单输入部分 -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/order_input_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:hint="关联订单"
app:layout_constraintEnd_toStartOf="@id/order_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title_text">
<!--订单文本框-->
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/order_input"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<!--扫码图标-->
<com.google.android.material.button.MaterialButton
android:id="@+id/order_button"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginStart="5dp"
android:background="@drawable/btn_s1"
app:backgroundTint="@null"
app:layout_constraintBottom_toBottomOf="@+id/order_input_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/order_input_layout"
app:layout_constraintTop_toTopOf="@+id/order_input_layout" />
<!-- 第二行:供应商和收货人 -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/supplier_input_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:hint="供应商"
app:layout_constraintEnd_toStartOf="@id/receiver_input_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/order_input_layout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/supplier_input"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/receiver_input_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="收货人"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/supplier_input_layout"
app:layout_constraintTop_toTopOf="@id/supplier_input_layout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/receiver_input"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/medicine_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="药品信息"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/supplier_input_layout" />
<TextView
android:id="@+id/TraceabilityCode_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="160dp"
android:text="请扫追溯码"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/medicine_label"
app:layout_constraintStart_toEndOf="@+id/medicine_label"
app:layout_constraintTop_toTopOf="@+id/medicine_label" />
<com.google.android.material.button.MaterialButton
android:id="@+id/TraceabilityCode_button"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginStart="10dp"
android:background="@drawable/btn_s1"
app:backgroundTint="@null"
app:layout_constraintBottom_toBottomOf="@+id/TraceabilityCode_label"
app:layout_constraintStart_toEndOf="@+id/TraceabilityCode_label"
app:layout_constraintTop_toTopOf="@+id/TraceabilityCode_label" />>
<!-- 表格标题行 -->
<GridLayout
android:id="@+id/table_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:background="#E0E0E0"
android:columnCount="5"
android:padding="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/medicine_label">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="3"
android:gravity="center"
android:text="商品名称"
android:textColor="#000000"
android:textSize="16sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="3"
android:gravity="center"
android:text="生产厂商"
android:textColor="#000000"
android:textSize="16sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="2"
android:gravity="center"
android:text="采购量"
android:textColor="#000000"
android:textSize="16sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="2"
android:gravity="center"
android:text="金额"
android:textColor="#000000"
android:textSize="16sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="2"
android:gravity="center"
android:text="追溯码"
android:textColor="#000000"
android:textSize="16sp" />
</GridLayout>
<!-- 表格内容 -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/medicine_recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:background="#FFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/table_header"
tools:listitem="@layout/item_medicine_row" />
<!-- 页尾部分 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/submit_button"
android:layout_width="200dp"
android:layout_height="50dp"
android:text="提交入库"
android:textColor="@color/white"
android:textSize="16sp"
android:gravity="center"
android:layout_marginBottom="16dp"
android:background="@drawable/button_selector"
app:cornerRadius="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/tv_result_final"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:textColor="@color/black"
android:textSize="17sp"
app:layout_constraintTop_toBottomOf="@+id/submit_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
三、创建数据库
五个表的逻辑也很简单,首先是drug_info表,这是送货单二维码的查询表,通过扫码送货单二维码,得到一串数字,拿这个数字作为参数访问API接口,得到送货单绑定的订单信息。
然后是medicine_trace表,这个表代表追溯码对应的商品信息。
orderform、orderitem、tracecode表是入库需要save的表,分别代表订单信息,药品列表信息和追溯码信息。
四、开发后端API
基于springboot框架和mybatis-plus进行开发设计,其中控制层也就是核心代码如下:
@RestController
public class test {
//从druginfo表查询数据
@Resource
private DrugInfoService drugInfoService;
@RequestMapping("/api/{qrCode}")
public R getDrugInfoByQrCode(@PathVariable String qrCode) {
List<DrugInfo> data = drugInfoService.getDrugInfoByQrCode(qrCode);
if (data != null){
System.out.println("查询被调用");
return R.OK(data);
}else {
return R.FAIL();
}
}
//从medicine_trace表查询数据,目的是获取追溯码代表的药品信息
@Resource
private MedicineTraceService medicineTraceService;
@Resource
private MedicineTrace_1 medicineTrace_1;
@RequestMapping("/api/tracecode/{traceCode}")
public R getMedicineByTracecode(@PathVariable String traceCode){
MedicineTrace medicineTrace = medicineTraceService.getById(traceCode);
if (medicineTrace != null){
System.out.println("药品查询被调用");
return R.OK(medicineTrace);
}else {
return R.FAIL();
}
}
//向Orderform表插入数据
@Resource
private OrderformService orderformService;
@PostMapping("/orderForm")
public R addOrder(@RequestBody Orderform orderform1){
boolean save = orderformService.save(orderform1);
System.out.println("送货单保存被调用");
return save?R.OK():R.FAIL();
}
//向orderitem插入数据
@Resource
private OrderitemService orderitemService;
@PostMapping("/orderItem")
public R addOrderItem(@RequestBody List<Orderitem> orderitems){
boolean save = orderitemService.saveBatch(orderitems);
System.out.println("药品清单保存被调用");
return save?R.OK():R.FAIL();
}
@Resource
private TracecodeService tracecodeService;
@PostMapping("/tracecode")
public R addTracecode(@RequestBody List<Tracecode> tracecodes){
boolean save = tracecodeService.saveBatch(tracecodes);
System.out.println("追溯码保存被调用");
return save?R.OK():R.FAIL();
}
}
package com.yz.demo.controller;
import com.yz.demo.Utils.OrderRequestDTO;
import com.yz.demo.domain.Orderform;
import com.yz.demo.domain.Orderitem;
import com.yz.demo.domain.Tracecode;
import com.yz.demo.result.R;
import com.yz.demo.service.OrderformService;
import com.yz.demo.service.OrderitemService;
import com.yz.demo.service.TracecodeService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;
@RestController
@RequestMapping("/order")
public class addData {
@Resource
private OrderformService orderformService;
@Resource
private OrderitemService orderitemService;
@Resource
private TracecodeService tracecodeService;
/**
* 保存订单,包括 Orderform、Orderitem 和 Tracecode 数据
*/
@PostMapping("/saveAll")
@Transactional // 开启事务
public R saveAll(@RequestBody OrderRequestDTO orderRequestDTO) {
try {
// Step 1: 保存 Orderform 表数据
Orderform orderform = orderRequestDTO.getOrderform();
boolean orderformSaved = orderformService.save(orderform);
if (!orderformSaved) {
throw new RuntimeException("保存 Orderform 数据失败");
}
// 获取自增的主键 ID
Long orderformId = orderform.getId();
// Step 2: 保存 Orderitem 表数据
List<Orderitem> orderitems = orderRequestDTO.getOrderitems();
for (Orderitem orderitem : orderitems) {
orderitem.setOrderId(orderformId); // 设置外键关联 Orderform 的 ID
}
boolean orderitemsSaved = orderitemService.saveBatch(orderitems);
if (!orderitemsSaved) {
throw new RuntimeException("保存 Orderitem 数据失败");
}
// 获取保存后的 Orderitem ID 列表(需要手动获取)
List<Long> orderItemIds = orderitems.stream()
.map(Orderitem::getId)
.toList();
// Step 3: 保存 Tracecode 表数据
List<Tracecode> tracecodes = orderRequestDTO.getTracecodes();
for (int i = 0; i < tracecodes.size(); i++) {
tracecodes.get(i).setOrderItemId(orderItemIds.get(i)); // 设置外键关联 Orderitem 的 ID
}
boolean tracecodesSaved = tracecodeService.saveBatch(tracecodes);
if (!tracecodesSaved) {
throw new RuntimeException("保存 Tracecode 数据失败");
}
// 如果全部保存成功,返回成功响应
System.out.println("全部保存已经调用");
return R.OK();
} catch (Exception e) {
e.printStackTrace();
// 如果出现异常,返回失败响应,并回滚事务
return R.FAIL(e.getMessage());
}
}
}
这样接口也就设计好了,可以通过以上的接口查询和保存数据。
五、AS环境设置
六、主程序代码
package com.example.myapplication;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.example.myapplication.RetrofitConfiguration.ManufacturerCallback;
import com.example.myapplication.RetrofitConfiguration.TraceCodeCallback;
import com.example.myapplication.Utils.TimeUtils;
import com.example.myapplication.bean.ApiResponse;
import com.example.myapplication.bean.ApiResponse_Medicine;
import com.example.myapplication.bean.DrugInfo;
import com.example.myapplication.bean.MedicineTrace;
import com.example.myapplication.bean.TraceCode;
import com.example.myapplication.domain.Medicine;
import com.example.myapplication.domain.MedicineAdapter;
import com.google.android.material.textfield.TextInputLayout;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity2 extends AppCompatActivity {
private RecyclerView medicineRecyclerView; //列表视图
private MedicineAdapter medicineAdapter; //适配器
private TextView scanResult_1; //
private TextView scanResult_2;
private TextView tv_result;
private int currentScanType;
private List<Medicine> list_medicine; //订单二维码扫描的结果,备份到这里,方便追溯码和这里的结果来对比
//接口
private final static String BaseURL = "http://192.168.123.108:8080/";
private final static String URL = BaseURL + "order/saveAll"; //这是用来保存订单表orderform的接口
private final static String URL_order = BaseURL + "api/"; //这是用来查询订单的接口
private final static String URL_tracecode = BaseURL + "api/tracecode/";
//为了区分两个不同的扫码按钮,定义以下两个常量
private static final int REQUEST_CODE_SCAN_BUTTON = 1001; //标识送货单扫码按钮
private static final int REQUEST_CODE_TRACECODE_BUTTON = 1002; //标识追溯码扫码按钮
private String scannedResult; //订单二维码扫码结果
private String medicineName_temp; //这是通过扫追溯码,获取到的药品名
private String manufacturer_temp; //这是通过扫追溯码,获取到的厂家名
private TextInputLayout textInputLayout; //收货人文本输入框的输入内容
private List<TraceCode> traceCodeList = new ArrayList<>();
@SuppressLint("MissingInflatedId")
@Override
//onCreate 是 Android 活动的生命周期方法之一。当活动被首次创建时,系统会调用这个方法。
//参数 savedInstanceState:如果活动之前的状态被保存了(比如屏幕旋转),该对象会包含之前的状态数据。可以用来恢复活动状态。
protected void onCreate(Bundle savedInstanceState) {
//调用父类的 onCreate 方法,以确保活动的基础初始化工作能够完成,比如创建窗口等。
super.onCreate(savedInstanceState);
//设置当前活动使用的布局文件为 res/layout/activity_main.xml。
setContentView(R.layout.activity_main);
medicineRecyclerView = findViewById(R.id.medicine_recycler_view);
medicineRecyclerView.setLayoutManager(new LinearLayoutManager(this));
Button scanButton = findViewById(R.id.order_button);
Button tracecodeButton = findViewById(R.id.TraceabilityCode_button);
//设置扫描按钮、追溯码扫码按钮的点击事件,点击之后开始QR扫码
scanButton.setOnClickListener(v -> {
startQRScanner();
currentScanType = REQUEST_CODE_SCAN_BUTTON;
});
tracecodeButton.setOnClickListener(v -> {
startQRScanner();
currentScanType = REQUEST_CODE_TRACECODE_BUTTON;
});
scanResult_1 = findViewById(R.id.order_input);
scanResult_2 = findViewById(R.id.supplier_input);
tv_result = findViewById(R.id.tv_result_final);
textInputLayout = findViewById(R.id.receiver_input_layout);
//设置提交按钮的点击事件
findViewById(R.id.submit_button).setOnClickListener(v -> {
postJson();
});
}
private void startQRScanner() {
// 启动二维码扫描
//在这行代码中,this 是指当前的 Activity,该 Activity 会被传递给 IntentIntegrator,它会在该 Activity 中启动扫码界面。
IntentIntegrator integrator = new IntentIntegrator(this);
//这一行代码设置了可以扫描的条形码类型
// ALL_CODE_TYPES 表示允许扫描所有类型的条形码和二维码。这样扫描时,无论是 QR 码还是条形码都能被识别。
integrator.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES);
//在扫描界面上,会显示这条提示信息,告诉用户如何进行操作。例如,提示用户将二维码或条形码置于扫描框内以开始扫描。
integrator.setPrompt("请将二维码或条形码置于扫描框内");
integrator.setCameraId(0); // 使用后置摄像头,1代表前置
integrator.setBeepEnabled(true); // 扫描成功提示音
integrator.setBarcodeImageEnabled(true); // 扫描结果保存为图片
//调用 initiateScan() 后,应用会跳转到扫码界面,允许用户通过摄像头扫描二维码或条形码。
// 一旦扫描到有效的二维码/条形码,扫码结果会传回 onActivityResult() 方法。
integrator.initiateScan();
}
//用于接收并处理从外部活动(比如扫码界面)返回的结果
//requestCode:请求码,在启动子活动时用来标识请求。
//resultCode:表示返回的结果状态。通常使用 RESULT_OK 或 RESULT_CANCELED 来表示操作是否成功。
//data:包含返回结果的 Intent 对象,通常包含额外的数据(比如扫码结果)。
@SuppressLint("SetTextI18n")
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null && result.getContents() != null && currentScanType == REQUEST_CODE_SCAN_BUTTON) {
//这里写扫订单二维码之后的代码
scannedResult = result.getContents().trim();
scanResult_1.setText(scannedResult);
scanResult_2.setText("国药控股扬州");
doGet(scannedResult);
} else if (result != null && result.getContents() != null && currentScanType == REQUEST_CODE_TRACECODE_BUTTON) {
TraceCode traceCode = new TraceCode(result.getContents().trim(), TimeUtils.getCurrentFormattedTime(), TimeUtils.getCurrentFormattedTime());
traceCodeList.add(traceCode);
//这里写扫追溯码之后的代码
medicineName_temp = null;
manufacturer_temp = null;
String sacannedResult_2 = result.getContents().trim();
//根据追溯码获取该药品的药品名和生产厂商
//这里用了回调
doGetTraceCode(sacannedResult_2, new TraceCodeCallback() {
@Override
public void onSuccess(MedicineTrace medicineTrace) {
runOnUiThread(() -> {
medicineName_temp = medicineTrace.getMedicineName();
manufacturer_temp = medicineTrace.getManufacturer();
int count = 0;
Medicine updatedMedicine = null;
for (Medicine item :
list_medicine) {
if (item.getName().equals(medicineName_temp) && item.getManufacturer().equals(manufacturer_temp)) {
//药品名和生产厂家都对上了,则需要在已扫描那里+1
String substring = item.getTraceCode().substring(4, 5);
int i = Integer.parseInt(substring) + 1;
item.setTraceCode("已扫描:" + i + "/" + item.getQuantity());
updatedMedicine = item;
break;
} else {
count++;
}
}
// UI 更新
if (count == list_medicine.size()) {
runOnUiThread(() -> {
tv_result.setText("药名:" + medicineName_temp + "不在清单");
});
} else {
medicineAdapter.updateItem(count, updatedMedicine);
}
});
}
@Override
public void onFailure(String errorMessage) {
runOnUiThread(() -> tv_result.setText("查询失败: " + errorMessage));
}
});
} else if (result == null && currentScanType == REQUEST_CODE_SCAN_BUTTON) {
scanResult_1.setText("扫描取消");
} else if (result.getContents() == null && currentScanType == REQUEST_CODE_SCAN_BUTTON) {
scanResult_1.setText("扫描内容为空");
} else {
scanResult_1.setText("出现未知异常");
}
}
//通过追溯码获取药品信息
private void doGetTraceCode(String qrcode_2, TraceCodeCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("Callback cannot be null");
}
String URL = URL_tracecode + qrcode_2;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
callback.onFailure("追溯码查询失败: " + e.getMessage());
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (!response.isSuccessful()) {
callback.onFailure("请求失败,HTTP状态码: " + response.code());
return;
}
String jsonResponse = null;
try {
if (response.body() == null) {
callback.onFailure("响应体为空");
return;
}
jsonResponse = response.body().string();
Gson gson = new Gson();
ApiResponse_Medicine apiResponse = gson.fromJson(jsonResponse, ApiResponse_Medicine.class);
if (apiResponse == null || apiResponse.getData() == null) {
throw new JsonSyntaxException("响应数据格式不正确或数据为空");
}
if (apiResponse.getCode() == 200) {
// 成功回调,传递解析后的数据
callback.onSuccess(apiResponse.getData());
} else {
callback.onFailure("查询失败,错误码: " + apiResponse.getCode());
}
} catch (JsonSyntaxException e) {
callback.onFailure("JSON解析失败: " + e.getMessage());
} catch (Exception e) {
callback.onFailure("未知错误: " + e.getMessage());
}
}
});
}
//这里是通过订单号二维码扫描结果,在网格列表中展示订单详细信息
private void doGet(String qrcode_1) {
String URL_orderForm = URL_order + qrcode_1;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL_orderForm)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(() -> {
tv_result.setText("请求失败: " + e.getMessage());
Log.e("doGet", "请求失败", e);
});
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
/* public boolean isSuccessful() {
return code >= 200 && code < 300;
} 需要先判断状态码是不是200-299*/
if (!response.isSuccessful()) {
runOnUiThread(() -> {
tv_result.setText("请求失败,HTTP状态码: " + response.code());
Log.e("doGet", "请求失败,HTTP状态码: " + response.code());
});
return;
}
String jsonResponse = null;
try {
jsonResponse = response.body().string();
Log.d("doGet", "接口返回: " + jsonResponse);
Gson gson = new Gson();
ApiResponse apiResponse = gson.fromJson(jsonResponse, ApiResponse.class);
if (apiResponse.getCode() == 200) {
List<DrugInfo> dataItems = apiResponse.getData();
List<Medicine> medicineList = new ArrayList<>();
for (DrugInfo item : dataItems) {
String drugName = item.getDrugName();
String manufacturer = item.getManufacturer();
int quantity = item.getQuantity();
double amount = item.getAmount();
String tracecodeTemplate = "已扫描:0/" + quantity;
medicineList.add(new Medicine(drugName, manufacturer, quantity, amount, tracecodeTemplate));
}
list_medicine = medicineList;
runOnUiThread(() -> {
medicineAdapter = new MedicineAdapter(medicineList);
medicineRecyclerView.setAdapter(medicineAdapter);
medicineRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity2.this));
tv_result.setText("请求成功,数据已更新");
});
} else {
runOnUiThread(() -> {
tv_result.setText("接口返回错误,Code: " + apiResponse.getCode());
Log.e("doGet", "接口返回错误,Code: " + apiResponse.getCode());
});
}
} catch (JsonSyntaxException e) {
Log.e("doGet", "JSON解析失败", e);
String finalJsonResponse = jsonResponse; // 捕获 JSON 数据用于调试
runOnUiThread(() -> tv_result.setText("JSON解析失败: " + e.getMessage() + "\n响应内容: " + finalJsonResponse));
} catch (Exception e) {
Log.e("doGet", "未知错误", e);
runOnUiThread(() -> tv_result.setText("未知错误: " + e.getMessage()));
}
}
});
}
private void postJson() {
String jsonString = "";
try {
// 获取当前时间,格式化为 yyyy-MM-dd HH:mm:ss
String currentTime = TimeUtils.getCurrentFormattedTime();
// 构建 Orderform 对象
JSONObject orderform = new JSONObject();
orderform.put("orderNumber", scannedResult);
String receiver = textInputLayout.getEditText() != null && textInputLayout.getEditText().getText() != null
? textInputLayout.getEditText().getText().toString()
: "";
orderform.put("supplier", "国药扬州");
orderform.put("receiver", receiver);
orderform.put("createdTime", currentTime);
orderform.put("updatedTime", currentTime);
// 检查药品清单和追溯码列表是否为空
if (list_medicine == null || list_medicine.isEmpty()) {
runOnUiThread(() -> tv_result.setText("药品清单为空"));
return;
}
if (traceCodeList == null || traceCodeList.isEmpty()) {
runOnUiThread(() -> tv_result.setText("追溯码列表为空"));
return;
}
// 构建 Orderitem 列表
JSONArray orderitems = new JSONArray();
for (Medicine medicine : list_medicine) {
JSONObject orderitem = new JSONObject();
orderitem.put("productName", medicine.getName());
orderitem.put("manufacturer", medicine.getManufacturer());
orderitem.put("quantity", medicine.getQuantity());
orderitem.put("amount", medicine.getAmount());
orderitem.put("createdTime", currentTime);
orderitem.put("updatedTime", currentTime);
orderitems.put(orderitem);
}
// 构建 Tracecode 列表
JSONArray tracecodes = new JSONArray();
for (TraceCode traceCode : traceCodeList) {
JSONObject tracecode = new JSONObject();
tracecode.put("traceCode", traceCode.getTraceCode());
tracecode.put("createdTime", traceCode.getCreatedAt());
tracecode.put("updatedTime", traceCode.getUpdatedAt());
tracecodes.put(tracecode);
}
// 构建最终的 OrderRequestDTO 对象
JSONObject orderRequestDTO = new JSONObject();
orderRequestDTO.put("orderform", orderform);
orderRequestDTO.put("orderitems", orderitems);
orderRequestDTO.put("tracecodes", tracecodes);
// 转换为字符串
jsonString = orderRequestDTO.toString();
} catch (Exception e) {
e.printStackTrace();
runOnUiThread(() -> tv_result.setText("JSON 构建失败:" + e.getMessage()));
return;
}
// 打印生成的 JSON,便于调试
System.out.println("最终生成的 JSON:" + jsonString);
// HTTP 请求
RequestBody body = RequestBody.create(jsonString, MediaType.parse("application/json;charset=utf-8"));
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.post(body)
.url(URL)
.addHeader("Content-Type", "application/json") // 确保请求头设置正确
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(() -> tv_result.setText("调用接口报错:" + e.getMessage()));
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body() != null ? response.body().string() : "";
if (response.isSuccessful()) {
try {
JSONObject jsonObject = new JSONObject(resp);
int code = jsonObject.optInt("code", -1);
String msg = jsonObject.optString("msg", "未返回消息");
runOnUiThread(() -> tv_result.setText("code: " + code + "\nmsg: " + msg));
} catch (JSONException e) {
e.printStackTrace();
runOnUiThread(() -> tv_result.setText("JSON 解析出错:" + e.getMessage()));
}
} else {
runOnUiThread(() -> tv_result.setText("接口调用失败,HTTP 状态码:" + response.code()));
}
}
});
}
}
这样,一个安卓系统的入库系统就做好了,可以说非常简单!