Java商城项目集成快递100物流追踪与运费计算方案
一、准备工作
-
注册快递100账号
- 访问快递100官网注册企业账号
- 获取API密钥(API Key)和客户编码(Customer)
-
添加依赖
<!-- Apache HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
二、物流追踪实现
1. 创建配置类
@Component
public class Kuaidi100Config {
@Value("${kuaidi100.customer}")
private String customer;
@Value("${kuaidi100.key}")
private String apiKey;
// Getter方法
}
2. 物流查询服务实现
@Service
public class LogisticsService {
@Autowired
private Kuaidi100Config config;
private static final String QUERY_URL = "https://poll.kuaidi100.com/poll/query.do";
public LogisticsResponse queryLogistics(String com, String num) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 1. 准备请求参数
Map<String, String> paramMap = new HashMap<>();
paramMap.put("com", com); // 快递公司编码
paramMap.put("num", num); // 快递单号
paramMap.put("phone", ""); // 收/寄件人手机号(可选)
String param = new ObjectMapper().writeValueAsString(paramMap);
String sign = DigestUtils.md5Hex(param + config.getApiKey() + config.getCustomer()).toUpperCase();
// 2. 构建POST请求
HttpPost httpPost = new HttpPost(QUERY_URL);
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("customer", config.getCustomer()));
params.add(new BasicNameValuePair("sign", sign));
params.add(new BasicNameValuePair("param", param));
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
// 3. 执行请求并解析响应
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
String json = EntityUtils.toString(response.getEntity());
return new ObjectMapper().readValue(json, LogisticsResponse.class);
}
} catch (Exception e) {
throw new RuntimeException("物流查询失败", e);
}
}
// 响应DTO
@Data
public static class LogisticsResponse {
private String message;
private String state;
private String status;
private String condition;
private String ischeck;
private List<LogisticsDetail> data;
@Data
public static class LogisticsDetail {
private String time;
private String ftime;
private String context;
private String location;
}
}
}
三、运费计算实现
1. 运费计算服务
@Service
public class FreightService {
@Autowired
private Kuaidi100Config config;
private static final String PRICE_URL = "https://www.kuaidi100.com/freight/query.do";
public FreightResponse calculateFreight(FreightRequest request) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 1. 准备请求参数
String param = new ObjectMapper().writeValueAsString(request);
String sign = DigestUtils.md5Hex(param + config.getApiKey() + config.getCustomer()).toUpperCase();
// 2. 构建POST请求
HttpPost httpPost = new HttpPost(PRICE_URL);
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("customer", config.getCustomer()));
params.add(new BasicNameValuePair("sign", sign));
params.add(new BasicNameValuePair("param", param));
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
// 3. 执行请求并解析
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
String json = EntityUtils.toString(response.getEntity());
return new ObjectMapper().readValue(json, FreightResponse.class);
}
} catch (Exception e) {
throw new RuntimeException("运费计算失败", e);
}
}
// 请求DTO
@Data
public static class FreightRequest {
private String sendProv; // 发货省
private String sendCity; // 发货市
private String recivProv; // 收货省
private String recivCity; // 收货市
private BigDecimal weight; // 重量(kg)
private String serviceType; // 服务类型 (标准快递/顺丰特惠等)
private String expressType; // 快递公司编码
}
// 响应DTO
@Data
public static class FreightResponse {
private String status;
private String message;
private List<FreightResult> data;
@Data
public static class FreightResult {
private String expressName; // 快递公司
private BigDecimal price; // 运费
private Integer time; // 预计时效(小时)
}
}
}
四、控制器层实现
@RestController
@RequestMapping("/logistics")
public class LogisticsController {
@Autowired
private LogisticsService logisticsService;
@Autowired
private FreightService freightService;
// 物流查询
@GetMapping("/track")
public LogisticsResponse track(
@RequestParam String expressCode,
@RequestParam String trackingNumber) {
return logisticsService.queryLogistics(expressCode, trackingNumber);
}
// 运费计算
@PostMapping("/calculate")
public FreightResponse calculate(@RequestBody FreightRequest request) {
return freightService.calculateFreight(request);
}
}
五、快递公司编码管理
@Service
public class ExpressCompanyService {
// 快递公司编码缓存(实际应从数据库读取)
private static final Map<String, String> COMPANY_CODES = Map.of(
"顺丰", "sf",
"中通", "zto",
"圆通", "yuantong",
"韵达", "yunda",
"京东", "jd"
);
public List<String> getAllCompanies() {
return new ArrayList<>(COMPANY_CODES.keySet());
}
public String getCompanyCode(String companyName) {
return COMPANY_CODES.get(companyName);
}
}
六、数据库设计建议
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(50) NOT NULL,
express_company VARCHAR(20) COMMENT '快递公司名称',
tracking_no VARCHAR(50) COMMENT '快递单号',
freight DECIMAL(10,2) COMMENT '运费'
);
CREATE TABLE logistics_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id BIGINT NOT NULL,
event_time DATETIME NOT NULL,
event_desc VARCHAR(200) NOT NULL,
location VARCHAR(100)
);
七、关键配置(application.yml)
kuaidi100:
customer: YOUR_CUSTOMER_CODE
key: YOUR_API_KEY
八、异常处理增强
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public Map<String, Object> handleServiceException(RuntimeException ex) {
return Map.of(
"code", 400,
"message", ex.getMessage(),
"timestamp", System.currentTimeMillis()
);
}
}
九、安全与优化措施
-
请求参数验证
public FreightResponse calculate(@Valid @RequestBody FreightRequest request) { // ... }
-
结果缓存
@Cacheable(value = "freightCache", key = "#request.hashCode()") public FreightResponse calculateFreight(FreightRequest request) { // ... }
-
限流保护
@RateLimiter(name = "kuaidi100Api", rate = 10) // 每秒10次 public LogisticsResponse queryLogistics(String com, String num) { // ... }
-
异步处理
@Async public CompletableFuture<LogisticsResponse> asyncQuery(String com, String num) { return CompletableFuture.completedFuture(queryLogistics(com, num)); }
十、前端集成示例
// 物流查询
axios.get('/logistics/track', {
params: {
expressCode: 'sf',
trackingNumber: 'SF123456789'
}
}).then(response => {
console.log('物流轨迹:', response.data);
});
// 运费计算
axios.post('/logistics/calculate', {
sendProv: '广东省',
sendCity: '深圳市',
recivProv: '浙江省',
recivCity: '杭州市',
weight: 1.5,
serviceType: '标准快递',
expressType: 'sf'
}).then(response => {
console.log('运费结果:', response.data);
});
实施注意事项:
- 快递公司编码:使用前需在快递100官网查询正确的快递公司编码
- 错误处理:正确处理API返回的
status
字段(如200
表示成功) - 超时设置:HttpClient需配置合理的连接超时和读取超时
- 敏感信息:API密钥应存储在配置中心或环境变量中
- 计费限制:注意快递100的API调用计费策略(免费额度+超额收费)
- 物流订阅:对大业务量建议使用订阅推送模式
提示:实际部署前需在测试环境充分验证,处理快递100返回的各种异常状态码(如
501
单号不存在、403
验证失败等)