最近项目需要些查询物流信息这个功能,刚开始是一脸懵逼,写什么,怎么写,需要哪些内容。。。?满脑子空白。上网一看各式各样、五花八门的写法,那么多,该选择哪一个呢?
开始之前先知道以下几个问题:
1.需要知道选择哪一家的api ? 快递100、快递鸟、阿里物流。。。
2.选择使用哪一个快递接口api ? 订阅推送api、实时查询api。。。
3.接口需要哪些入参,入参类型;接口返回值?
选择:选择快递100家的api,订阅推送api。
思路想法:项目的物流信息通过访问快递100api获取物流信息保存到数据库中,当需要查询物流信息的时候,是从数据库里取数据,而不是从快递100那边再获取。
现在已经选择好了,记得在数据库建一张物流信息表:

现在开始撸代码了:
1.开始订阅物流信息,输入快递单号和快递公司的Code,开始订阅物流信息,当发现有新的物流信息的时候,快递100会通过回调Url返回物流信息,则在回调接口吧物流信息保存到数据库中。下面是conroller
import com.hczt.mall.modules.service.logistics.LogisticsService;
import com.hczt.mall.modules.vo.logistics.NoticeResponse;
import com.hczt.mall.modules.vo.logistics.TaskRequest;
import com.hczt.mall.result.RtnResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@Slf4j
public class LogisticsController {
@Autowired
private LogisticsService logisticsService;
/**
* 订单订阅物流信息
*
* @param req
* @return
*/
@PostMapping("/api/logistics/subscrible/{orderId}")
public RtnResult<Object> subscribleLogistics(@RequestBody TaskRequest req, @PathVariable long orderId) {
return logisticsService.subscribleLogistics(req, orderId);
}
}
订阅service层: 请求参数快递单号和快递公司code不能为空。
public RtnResult<Object> subscribleLogistics(TaskRequest req, long orderId) {
//回调url
String url = urlHeader.concat(Constants.LogisticsConstants.CALLBACK_URL_MIDDLE).concat(String.valueOf(orderId));
req.getParameters().put("callbackurl", url);
req.setKey(kuaidi100Key);
Map<String, String> p = new HashMap<String, String>();
p.put("schema", "json");
p.put("param", JacksonHelper.toJSON(req));
log.info("物流信息订阅开始,订单号:【{}】,入参为:【{}】", orderId, p);
try {
String ret = HttpRequest.postData("http://www.kuaidi100.com/poll", p, "UTF-8");
TaskResponse resp = JacksonHelper.fromJSON(ret, TaskResponse.class);
if (resp.getResult() != true) {
if (resp.getReturnCode().equals("600") || resp.getReturnCode().equals("601") || resp.getReturnCode().equals("500")) {
CodeMsg codeMsg = CodeMsg.SUBSCRIBLE_FAIL;
codeMsg = codeMsg.addData(resp.getMessage());
log.error("订阅物流信息失败,订单号为:【{}】,失败原因为:【{}】", orderId, resp.getMessage());
return RtnResult.error(codeMsg);
} else {
log.error("订阅物流信息失败,订单号为:【{}】,失败原因为:【{}】", orderId, resp.getMessage());
return RtnResult.success(resp.getMessage());
}
} else {
//同步订单的快递单号,快递公司code,修改订单状态为已发货
Optional<Order> orderOptional = orderRepository.findById(orderId);
if (orderOptional.isPresent()) {
Order order = orderOptional.get();
order.setOrderLogisticsNo(req.getNumber());
order.setExpressCompanyCode(req.getCompany());
order.setOrderStatus(Constants.OrderConstants.TradeState.SENDED);
orderRepository.save(order);
}
log.info("订阅物流信息成功,订单号为:【{}】,物流单号为:【{}】", orderId, req.getNumber());
return RtnResult.success("success");
}
} catch (Exception e) {
throw new BusinessException("", e);
}
}
2.订阅后的回调接口:把物流信息解析直接一整个保存到数据库,当查询的时候在吧数据做解析。
注意:回调结果要有返回值,无论成功或者失败,都要返回result、returnCode、message。
返回实例:成功一定要有返回值,不然回重发推送。
成功:
//要返回成功(格式与订阅时指定的格式一致),不返回成功就代表失败,没有这个30分钟以后会重推
'{"result":"true", "returnCode":"200","message":"成功"}';
失败://保存失败,返回失败信息,30分钟以后会重推
'{"result":"false", "returnCode":"500","message":"失败"}';
controller层:
/**
* 快递结果回调接口
*
* @param request
* @return
*/
@PostMapping("/logistics/callback/{orderId}")
public NoticeResponse expressCallback(HttpServletRequest request, @PathVariable String orderId) {
String param = request.getParameter("param");
log.info("订单物流回调开始,入参为:" + param);
return logisticsService.updateExpressInfo(param, orderId);
}
service层:
@Transactional
public NoticeResponse updateExpressInfo(String param, String orderId) {
NoticeResponse resp = new NoticeResponse();
try {
NoticeRequest nReq = JacksonHelper.fromJSON(param, NoticeRequest.class);
if (!"abort".equals(nReq.getStatus())) {
Result result = nReq.getLastResult();
// 运单号
String logisticsNo = result.getNu();
// 快递公司编码
String expressCompanyCode = result.getCom();
// 快递单当前签收状态
String status = result.getState();
// 是否签收标记
String isCheck = result.getIscheck();
String data = JacksonHelper.toJSON(result.getData());
//根据物流单号获取原来的物流信息,首次记录是返回null
OrderLogistics oldOrderLogistics = orderLogisticsRepository.findAllByOrderId(Long.valueOf(orderId));
//根据物流单号删除物流信息,首次记录是返回0
int changeRows = orderLogisticsRepository.deleteByLogisticsNo(logisticsNo);
//根据物流单号和订单号查询订单
Order order = orderRepository.findByOrderIdAndOrderLogisticsNo(Long.valueOf(orderId), logisticsNo);
ExpressCompanyDict expressCompanyDict = expressCompanyDictRepository.findByExpressCompanyCode(expressCompanyCode);
// 快递公司名称
String expressCompanyName = expressCompanyDict.getExpressCompanyName();
OrderLogistics orderLogistics = new OrderLogistics();
orderLogistics.setOrderId(Long.valueOf(orderId));
orderLogistics.setLogisticsNo(logisticsNo);
orderLogistics.setStatus(status);
orderLogistics.setExpressCompanyCode(expressCompanyCode);
orderLogistics.setExpressCompanyName(expressCompanyName);
orderLogistics.setData(data);
orderLogistics.setIsCheck(isCheck);
orderLogisticsRepository.save(orderLogistics);
log.info("订单物流回调成功,订单物流信息为:【{}】", orderLogistics);
if (changeRows != 0) {
log.info("订单物流回调,旧的物流信息为:【{}】,新的物流信息为:【{}】", oldOrderLogistics, orderLogistics);
}
resp.setResult(true);
resp.setReturnCode("200");
resp.setMessage("成功");
}
} catch (Exception e) {
resp.setResult(false);
resp.setReturnCode("500");
resp.setMessage("保存失败" + e);
log.error("订单物流回调失败,失败信息为:【{}】", resp);
}
return resp;
}
3.查询物流信息:根据订单号查询物流信息
conroller层:
@GetMapping("/api/logistics/detail/{orderId}")
public RtnResult<Object> getLogistics(@PathVariable long orderId) {
return logisticsService.getLogistics(orderId);
}
service层:
public RtnResult<Object> getLogistics(long orderId) {
OrderLogistics orderLogistics = orderLogisticsRepository.findAllByOrderId(orderId);
Optional<Order> optionalOrder = orderRepository.findById(orderId);
if (!optionalOrder.isPresent()) {
return RtnResult.error(CodeMsg.ORDER_NOT_EXIST);
}
Order order = optionalOrder.get();
if (orderLogistics == null) {
return RtnResult.error(CodeMsg.LOGISTICS_NOT_EXIST);
}
//解析物流信息
JSONArray obj = JSONArray.parseArray(orderLogistics.getData());
// int index = 1;
// for (Object o : obj) {
// Map<String, Object> m = (Map) o;
// m.put("index", index++);
// }
Map<String, Object> map = new HashMap<>();
map.put("expressCompanyCode", orderLogistics.getExpressCompanyCode());
map.put("expressCompanyName", orderLogistics.getExpressCompanyName());
map.put("logisticsNo", orderLogistics.getLogisticsNo());
map.put("address", order.getAddress());
map.put("logisticsData", obj);
map.put("statusCode", orderLogistics.getStatus());
map.put("statusName", LogisticsStatusEnum.getValueByCode(orderLogistics.getStatus()));
map.put("isCheck", orderLogistics.getIsCheck());
map.put("receiveName", order.getReceiverName());
map.put("receiveTel", order.getReceiverTel());
return RtnResult.success(map);
}
postman测试:
