BMS TMS 计费批量数据测试接口 demo
This commit is contained in:
+288
@@ -0,0 +1,288 @@
|
||||
package com.ruoyi.web.controller.tool;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamConstants;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
|
||||
/**
|
||||
* BMS TMS 计费接口 demo(单文件,纯 JDK,无第三方依赖)
|
||||
*
|
||||
* 功能:读取 D:\code\view\orders_10000.xlsx 的每一行(跳过表头),
|
||||
* 组装为 JSON 后 POST 到本地 BMS 计费接口。
|
||||
*
|
||||
* 接口:POST http://127.0.0.1:8080/api/tms/price/bill
|
||||
* 鉴权:Header X-Api-Code
|
||||
*
|
||||
* 编译运行:
|
||||
* javac TmsPriceBillDemo.java
|
||||
* java TmsPriceBillDemo
|
||||
*/
|
||||
public class TmsPriceBillDemo {
|
||||
|
||||
// ===== 可配置项 =====
|
||||
private static final String BASE_URL = "http://127.0.0.1:8080";
|
||||
private static final String BILL_PATH = "/api/tms/price/bill";
|
||||
private static final String API_CODE = "BMS.TMS.ZYaOvlQzpcfau8TUaCFfO3Hp8ieHIqGw";
|
||||
private static final String EXCEL_PATH = "D:\\code\\view\\orders_10000.xlsx";
|
||||
private static final int CONNECT_TIMEOUT_MS = 10_000;
|
||||
private static final int READ_TIMEOUT_MS = 30_000;
|
||||
/** 每多少条打印一次进度 */
|
||||
private static final int PROGRESS_STEP = 200;
|
||||
|
||||
/** 数值字段:JSON 中不加引号,直接输出 Excel 中的原始数字字符串 */
|
||||
private static final Set<String> NUMERIC_FIELDS = new HashSet<>(Arrays.asList(
|
||||
"weight", "pieces", "actualWeight", "volume", "volumeWeight",
|
||||
"pkgLength", "pkgWidth", "pkgHeight", "tmsId"
|
||||
));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 可通过命令行参数覆盖 Excel 路径
|
||||
String excel = (args.length > 0 && !args[0].isEmpty()) ? args[0] : EXCEL_PATH;
|
||||
System.out.println("[config] base=" + BASE_URL + BILL_PATH);
|
||||
System.out.println("[config] xApiCode=" + API_CODE);
|
||||
System.out.println("[config] excel=" + excel);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
int ok = 0, fail = 0, rowIdx = 0;
|
||||
|
||||
try (ZipFile zf = new ZipFile(excel)) {
|
||||
List<String> sst = readSharedStrings(zf);
|
||||
System.out.println("[read] sharedStrings uniqueCount=" + sst.size());
|
||||
|
||||
SheetReader sr = new SheetReader(zf, sst);
|
||||
String[] headers = null;
|
||||
List<String> row;
|
||||
|
||||
while ((row = sr.nextRow()) != null) {
|
||||
rowIdx++;
|
||||
if (headers == null) {
|
||||
headers = row.toArray(new String[0]);
|
||||
System.out.println("[read] headers=" + row);
|
||||
continue;
|
||||
}
|
||||
String json = buildJson(row, headers);
|
||||
String resp;
|
||||
try {
|
||||
resp = postJson(BASE_URL + BILL_PATH, API_CODE, json);
|
||||
} catch (Exception e) {
|
||||
resp = "ERR: " + e.getMessage();
|
||||
}
|
||||
if (resp.contains("\"code\":200") || resp.contains("\"code\": 200")) {
|
||||
ok++;
|
||||
} else {
|
||||
fail++;
|
||||
}
|
||||
if (rowIdx % PROGRESS_STEP == 0 || rowIdx == 1) {
|
||||
String snippet = resp.length() > 120 ? resp.substring(0, 120) + "..." : resp;
|
||||
System.out.printf("[progress] %d ok=%d fail=%d last=%s%n", rowIdx, ok, fail, snippet);
|
||||
}
|
||||
}
|
||||
System.out.printf("[done] rows=%d ok=%d fail=%d cost=%dms%n",
|
||||
rowIdx, ok, fail, System.currentTimeMillis() - start);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== JSON 构造 =====
|
||||
|
||||
private static String buildJson(List<String> row, String[] headers) {
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append('{');
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
if (i > 0) sb.append(',');
|
||||
String k = headers[i];
|
||||
String v = (i < row.size()) ? row.get(i) : "";
|
||||
sb.append('"').append(escape(k)).append("\":");
|
||||
boolean isNumeric = NUMERIC_FIELDS.contains(k)
|
||||
&& v != null && !v.isEmpty() && !"null".equals(v);
|
||||
if (isNumeric) {
|
||||
sb.append(v);
|
||||
} else if (v == null || v.isEmpty() || "null".equals(v)) {
|
||||
sb.append("null");
|
||||
} else {
|
||||
sb.append('"').append(escape(v)).append('"');
|
||||
}
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String escape(String s) {
|
||||
if (s == null) return "";
|
||||
StringBuilder sb = new StringBuilder(s.length());
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '"': sb.append("\\\""); break;
|
||||
case '\\': sb.append("\\\\"); break;
|
||||
case '\n': sb.append("\\n"); break;
|
||||
case '\r': sb.append("\\r"); break;
|
||||
case '\t': sb.append("\\t"); break;
|
||||
default:
|
||||
if (c < 0x20) sb.append(String.format("\\u%04x", (int) c));
|
||||
else sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// ===== HTTP =====
|
||||
|
||||
private static String postJson(String url, String apiCode, String json) throws Exception {
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
|
||||
conn.setReadTimeout(READ_TIMEOUT_MS);
|
||||
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
|
||||
conn.setRequestProperty("X-Api-Code", apiCode);
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
|
||||
byte[] body = json.getBytes(StandardCharsets.UTF_8);
|
||||
conn.setRequestProperty("Content-Length", String.valueOf(body.length));
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(body);
|
||||
os.flush();
|
||||
}
|
||||
|
||||
int code = conn.getResponseCode();
|
||||
InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
|
||||
String respBody = readAll(is);
|
||||
return "HTTP " + code + " " + respBody;
|
||||
} finally {
|
||||
if (conn != null) conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private static String readAll(InputStream is) throws Exception {
|
||||
if (is == null) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (sb.length() > 0) sb.append('\n');
|
||||
sb.append(line);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// ===== XLSX 解析(纯 JDK) =====
|
||||
|
||||
/** 读取 xl/sharedStrings.xml,返回共享字符串表 */
|
||||
private static List<String> readSharedStrings(ZipFile zf) throws Exception {
|
||||
ZipEntry e = zf.getEntry("xl/sharedStrings.xml");
|
||||
if (e == null) return Collections.emptyList();
|
||||
List<String> out = new ArrayList<>();
|
||||
try (InputStream in = zf.getInputStream(e)) {
|
||||
XMLInputFactory f = XMLInputFactory.newInstance();
|
||||
// 禁用 DTD 以避免 XXE
|
||||
f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
|
||||
f.setProperty(XMLInputFactory.SUPPORT_DTD, false);
|
||||
XMLStreamReader r = f.createXMLStreamReader(in);
|
||||
StringBuilder text = new StringBuilder();
|
||||
boolean inT = false;
|
||||
while (r.hasNext()) {
|
||||
int ev = r.next();
|
||||
if (ev == XMLStreamConstants.START_ELEMENT) {
|
||||
if ("t".equals(r.getLocalName())) {
|
||||
inT = true;
|
||||
text.setLength(0);
|
||||
}
|
||||
} else if (ev == XMLStreamConstants.CHARACTERS) {
|
||||
if (inT) text.append(r.getText());
|
||||
} else if (ev == XMLStreamConstants.END_ELEMENT) {
|
||||
String ln = r.getLocalName();
|
||||
if ("t".equals(ln)) {
|
||||
inT = false;
|
||||
} else if ("si".equals(ln)) {
|
||||
out.add(text.toString());
|
||||
text.setLength(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
r.close();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 流式读取 xl/worksheets/sheet1.xml,nextRow() 逐行返回 */
|
||||
static class SheetReader {
|
||||
private final XMLStreamReader r;
|
||||
private final List<String> sst;
|
||||
private final InputStream stream;
|
||||
|
||||
SheetReader(ZipFile zf, List<String> sst) throws Exception {
|
||||
ZipEntry e = zf.getEntry("xl/worksheets/sheet1.xml");
|
||||
this.stream = zf.getInputStream(e);
|
||||
this.sst = sst;
|
||||
XMLInputFactory f = XMLInputFactory.newInstance();
|
||||
f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
|
||||
f.setProperty(XMLInputFactory.SUPPORT_DTD, false);
|
||||
this.r = f.createXMLStreamReader(stream);
|
||||
}
|
||||
|
||||
List<String> nextRow() throws Exception {
|
||||
List<String> row = null;
|
||||
String curType = null;
|
||||
StringBuilder curVal = new StringBuilder();
|
||||
boolean inV = false;
|
||||
while (r.hasNext()) {
|
||||
int ev = r.next();
|
||||
if (ev == XMLStreamConstants.START_ELEMENT) {
|
||||
String ln = r.getLocalName();
|
||||
if ("row".equals(ln)) {
|
||||
row = new ArrayList<>();
|
||||
} else if ("c".equals(ln) && row != null) {
|
||||
curType = r.getAttributeValue(null, "t");
|
||||
curVal.setLength(0);
|
||||
} else if ("v".equals(ln) && row != null) {
|
||||
inV = true;
|
||||
} else if ("t".equals(ln) && row != null) {
|
||||
inV = true; // inlineStr 的 <t>
|
||||
}
|
||||
} else if (ev == XMLStreamConstants.CHARACTERS) {
|
||||
if (inV && row != null) curVal.append(r.getText());
|
||||
} else if (ev == XMLStreamConstants.END_ELEMENT) {
|
||||
String ln = r.getLocalName();
|
||||
if ("v".equals(ln) || "t".equals(ln)) {
|
||||
inV = false;
|
||||
} else if ("c".equals(ln) && row != null) {
|
||||
String v = curVal.toString();
|
||||
String val;
|
||||
if ("s".equals(curType)) {
|
||||
int idx = Integer.parseInt(v.trim());
|
||||
val = idx < sst.size() ? sst.get(idx) : "";
|
||||
} else if ("b".equals(curType)) {
|
||||
val = "1".equals(v.trim()) ? "true" : "false";
|
||||
} else {
|
||||
// 数值、str、inlineStr、空等,直接取原值
|
||||
val = v;
|
||||
}
|
||||
row.add(val);
|
||||
} else if ("row".equals(ln) && row != null) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
try { r.close(); stream.close(); } catch (Exception ignored) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user