快递的物流地图是怎么实现的

踩坑总结

1.接口都是使用的表单数据、且推送接口表单数据带空格需要处理一下、(当时怎么都获取不到数据)

2.地图并不是查询的实时的、而是根据你订阅接口发送的发货地和收货地来绘制的、如果想要获取准确的地图需要额外处理。

3.推送接口推的数据经常不返回地图的url、这里可以处理一下、增加个判断、这样快递100会等待半个小时之后再推送一次。

4.顺丰快递需要手机号才能查到

5.订阅的时候resultv2的值别用默认的、可以用5或者7

<"will.h8m6.cn"><"eliza.h8m6.cn"><"han.h8m6.cn"><"leia.h8m6.cn">

<"luke.h8m6.cn"><"rey.h8m6.cn"><"finn.h8m6.cn"><"poe.h8m6.cn">

6.我们使用的快递100的api、地图url在未签收前有三天有效期、签收后就只有15天了、如果想要持久化物流信息的话、最好还是拿他们返回的地理位置自己进行渲染、如果觉得不影响、可以直接使用他们返回的url。


if param.
LastResult_.
TrailUrl == 
"" {
    ctx.
JSON(
500, gin.
H{
        
"result":     
false,
        
"returnCode": 
"500",
        
"message":    
"trailUrl数据为空",
    })

1、基本信息

接口文档

api.kuaidi100.com/document/60…

一个订阅接口一个推送接口

1.1订阅接口

需要发送字段

<"kylo.h8m6.cn"><"obi.h8m6.cn"><"yoda.h8m6.cn"><"maul.h8m6.cn">

<"dooku.h8m6.cn"><"jango.h8m6.cn"><"boba.h8m6.cn"><"chewy.h8m6.cn">

参数名 是否必填 类型 说明
key string 授权码,请申请企业版获取
company string 订阅的快递公司的编码,一律用小写字母
number string 订阅的快递单号,单号的最大长度是40个字符
from string 快递寄件地址
to string 快递收件地址
parameters Object 辅助参数

parameters数据结构:

参数名 是否必填 类型 说明
callbackurl string 回调接口的地址
salt string 签名用随机字符串
phone string 收寄件人的移动电话号码(只能填写一个,顺丰速运和丰网速运必填,其他快递公司选填)
ordertime string 订单下单时间,格式“yyyy-MM-dd HH”
resultv2 string 添加此字段表示开通行政区域解析功能。0:关闭(默认),1:开通行政区域解析功能以及物流轨迹增加物流状态值,2:开通行政解析功能以及物流轨迹增加物流状态值并且返回出发、目的及当前城市信息 4:开通行政解析功能以及物流轨迹增加物流高级状态名称并且返回出发、目的及当前城市信息 6:开通行政解析功能以及物流轨迹增加物流高级状态名称、状态值并且返回出发、目的及当前城市信息

1.2推送接口

接收的字段

项目 值/说明
请求方式 POST
Header参数 Content-Type: application/x-www-form-urlencoded
Body参数 见下表

Body参数

参数名 必填 类型 说明
sign string 加密签名(需转32位大写),算法: md5(param+salt)(仅当salt值非空时推送)
param Object 主体参数对象

param对象结构

字段名 必填 类型 说明 可选值
status String 监控状态 polling(监控中)、 shutdown(结束)、 abort(中止)、 updateall(重新推送)
billstatus String 已弃用,忽略即可 got, sending, check
message String 状态消息(如: 3天查询无记录, 60天无变化
autoCheck String 快递公司编码纠错标记 0(原始编码)、 1(纠正后的编码)
comOld String 原始快递公司编码(当autoCheck=1或status=abort时有效;国际版接口无此字段) 如: yuantong
comNew String 纠正后的快递公司编码(当autoCheck=1时有效;国际版接口无此字段) 如: ems
lastResult Object 最新查询结果数据

lastResult对象结构

<"lando.h8m6.cn"><"t800.h8m6.cn"><"terminator.h8m6.cn"><"john.h8m6.cn">

<"sarah.h8m6.cn"><"marty.h8m6.cn"><"doc.h8m6.cn"><"biff.h8m6.cn">

字段名 类型 说明 特殊条件
message String 消息体(忽略)
state String 核心状态:0在途, 1揽收, 2疑难, 3签收, 4退签, 5派件, 8清关, 14拒签
status String 通讯状态(忽略)
condition String 明细状态标记(未实现)
ischeck Integer 签收标记(0未签收, 1已签收,建议用state字段替代)
com String 快递公司编码(小写)
nu String 快递单号
trailUrl String 轨迹地图链接 resultv2=7时值为null
arrivalTime String 预计到达时间
totalTime String 平均耗时
remainTime String 剩余时间
isLoop Boolean 是否存在运输环路
routeInfo Object 行政区划路由信息
data Array 物流轨迹详情(倒序排列)
predictedRoute Array 物流节点预测数据 需传参resultv2=7

routeInfo(行政区划路由)

字段名 子字段 类型 说明
from number String 出发地行政区编码
name String 出发地名称
cur number String 当前地行政区编码
name String 当前地名称
to number String 目的地行政区编码
name String 目的地名称

data(物流轨迹详情)

字段名 类型 说明 触发条件
context String 物流信息内容
time String 原始时间(如:2025-04-16 10:36:46)
ftime String 格式化后时间
status String 物流状态名称 resultv2=3或5
areaCode String 行政区域编码 resultv2=3或5
areaName String 行政区域名称 resultv2=3或5
statusCode String 高级物流状态值 resultv2=5
areaCenter String 行政区域经纬度(高德坐标) resultv2=5
location String 快件当前位置 resultv2=5
areaPinYin String 行政区域拼音 resultv2=5

predictedRoute(物流节点预测)

字段名 类型 说明 示例值
arriveTime String 到达节点时间 2025-04-16 10:36:46
leaveTime String 离开节点时间 2025-04-16 10:39:03
province String 节点所在省 湖北
city String 节点所在市 宜昌市
district String 节点所在区 点军区
name String 节点名称 武汉转运中心
state String 节点状态: 已经过/ 当前停留/ 预估途径
type String 节点类型: 转运中心/ 网点
location String 经纬度坐标(高德)

1.2.1关键业务逻辑说明(如果业务要求不高就暂时不需要处理)

  1. 状态关联
  • 当快递签收 → status="shutdown"
  • 当连续60天无变化/3天无记录 → status="abort"(需特殊处理)
  1. 公司编码纠错
  • 连续3天查不到结果时:
  • 若原始编码正确 → 推送 autoCheck=0
  • 若编码错误 → 自动用新编码订阅,并推送:
    autoCheck=1, comOld=原编码, comNew=新编码
  1. 高级字段触发
  • 需传参 resultv2=3/5/7 才会返回高级字段(如statusCode/predictedRoute)。

2、表结构

2.1订阅接口

为什么要这么设计

一般来说、当我们把商品出仓或者入仓的时候、需要追踪物流、来查看当前商品的运输情况、这里只讨论出仓的时候、入仓也是一样的逻辑。

<"indiana.h8m6.cn"><"marion.h8m6.cn"><"et.h8m6.cn"><"elliott.h8m6.cn">

<"chihiro.h8m6.cn"><"totoro.h8m6.cn"><"ponyo.h8m6.cn"><"howl.h8m6.cn">

2.1.1.基础字段

key、company、number、from、to、parameters、Object、callbackurl(地址我做了三级下拉框所以一共是6个字段)

这几个是必填的、需要我们发送到快递100那边、对快递进行订阅、

id 、created_at 、updated_at 、created_user_id 、created_user_nickname 、updated_user_id 、updated_user_nickname 、deleted 、organization_id

这几个字段是基础字段、一般创建表的时候都要包含这几个字段

2.1.2特殊字段

另外还有一些特殊字段、因为出库单的上游可能是不同类型的单据、比如销售的、采购退货的、所以需要一个type来表示不同的单据、一个relevance_id来跟其他单据做关联、另外需要记录一下你推送给快递100的推送状态、所以我设计了map_tracker_status 这个字段、下图为出库单的表结构(按需修改)

create table 
sims_enter_leave_inventory_bill

(
  id                      bigint auto_increment
  primary key,
  created_at              datetime       
default CURRENT_TIMESTAMP 
null,
  updated_at              datetime       
default CURRENT_TIMESTAMP 
null,
  created_user_id         bigint                                   
null,
  created_user_nickname   varchar(
500)                             
null,
  updated_user_id         bigint                                   
null,
  updated_user_nickname   
varchar
(
500)                             
null,
  deleted                 
int
(
1)         
default 
0                 
null,
  organization_id         bigint                                   
null,

  name                    
varchar
(
200)                             
null comment 
'单据名称',
  code                    
varchar
(
500)                             
null,
  direction               
varchar
(
10)                              
null comment 
'0:入库 1:出库',
  type                    
varchar
(
10)                              
null comment 
'【入库】0:进销存售后单 1:采购订单 2:人工新批次 3:人工已出批次 4:组合单 5:销售订单耗用 6:商城售后单 【出库】0:进销存订单 1:采购售后 2:人工出库 3:组合单 5:销售订单耗用 6:商城订单',
  relevance_id            bigint                                   
null,
  relevance_code          
varchar
(
500)                             
null,
  counterparty_id         bigint                                   
null comment 
'供应商Id',
  status                  
varchar
(
10)                              
null comment 
'【入库】0:待确认 1:待入库 2:已完成 3:取消 4:失败  【出库】0:待确认 1:待出库 2:待收货 3:已完成 4:取消 5:失败',
  warehouse_id            bigint                                   
null,
  product_num             bigint                                   
null,
  delivery_company        
varchar
(
500)                             
null,
  delivery_num            
varchar
(
500)                             
null,
  map_tracker_status      
varchar
(
20)    
default 
'0'               
null comment 
'地图轨迹推送状态: 0=未推送, 1=已推送, 2=推送失败',
  message                 text                                     
null,
  remark                  
varchar
(
200)                             
null comment 
'备注',
  operate_date            
varchar
(
50)                              
null comment 
'操作时间',
  price_tax_expenses      
decimal
(
18, 
2) 
default 
0.00              
null comment 
'整单分摊的商品价格+税',
  additional_tax_expenses 
decimal
(
18, 
2) 
default 
0.00              
null comment 
'整单分摊的附加费+税',
  product_tax_expenses    
decimal
(
18, 
2) 
default 
0.00              
null comment 
'整单分摊的商品税',
  purchase_tax_expenses   
decimal
(
18, 
2) 
default 
0.00              
null comment 
'整单分摊的附加费税',
  sale_price              
varchar
(
250)   
default 
'0.00'            
null comment 
'销售单价',
  sale_price_decimal      
decimal
(
18, 
2) 
default 
0.00              
null comment 
'售后-销售总价',
  income_expend_type      
varchar
(
4)                               
null comment 
'类型 1-非订单收入 2-股东投入分配 3-非订单费用支出',
  total_amount            
decimal
(
18, 
2) 
default 
0.00              
null comment 
'人工出入库单据总金额',
  product_total_amount    
decimal
(
18, 
2) 
default 
0.00              
null comment 
'出入库单据商品总金额',
    order_expense_category  
varchar
(
255)                             
null comment 
'订单支出类别',
    delivery_province_code  
varchar
(
255)                             
null comment 
'收货地址省份编码',
    delivery_city_code      
varchar
(
255)                             
null comment 
'收货地址城市编码',
    delivery_area_code      
varchar
(
255)                             
null comment 
'收货地址区县编码',
    ship_province_code      
varchar
(
255)                             
null comment 
'发货地址省份编码',
    ship_city_code          
varchar
(
255)                             
null comment 
'发货地址城市编码',
    ship_area_code          
varchar
(
255)                             
null comment 
'发货地址区县编码',
    phone_number            
varchar
(
255)                             
null comment 
'物流电话'
)
    charset = utf8mb4;

create index relevance_type_idx
    on 
sims_enter_leave_inventory_bill 
(direction, type, relevance_id);

2.2推送接口

2.2.1基础字段

同订阅接口

2.2.2特殊字段

create table soms.express_tracking
(
    id                        bigint unsigned auto_increment comment 
'主键ID'
        primary key,
    express_no                
varchar
(
40)                                                                  not 
null comment 
'快递单号',
    express_company           
varchar
(
20)                                                                  not 
null comment 
'快递公司编码',
    request_sign              
varchar
(
64)                                                                  
null comment 
'请求签名(md5(param+salt))',
    auto_check                tinyint                                            
default 
0                 
null comment 
'公司编码是否纠正(0:未纠正,1:已纠正)',
    original_express_company  
varchar
(
20)                                                                  
null comment 
'原始快递公司编码',
    corrected_express_company 
varchar
(
20)                                                                  
null comment 
'纠正后的快递公司编码',
    track_status              
enum 
(
'polling', 
'shutdown', 
'abort', 
'updateall') 
default 
'polling'         
null comment 
'跟踪状态',
    track_message             
varchar
(
255)                                                                 
null comment 
'跟踪状态消息',
    express_state             
int                                                                          
null comment 
'运单状态值',
    advanced_status           
varchar
(
50)                                                                  
null comment 
'高级状态名称',
    advanced_status_code      
int                                                                          
null comment 
'高级状态值',
    current_location          
varchar
(
100)                                                                 
null comment 
'当前位置描述',
    area_code                 
varchar
(
20)                                                                  
null comment 
'行政区编码',
    area_name                 
varchar
(
50)                                                                  
null comment 
'行政区名称',
    route_from_code           
varchar
(
20)                                                                  
null comment 
'出发地行政区编码',
    route_from_name           
varchar
(
50)                                                                  
null comment 
'出发地行政区名称',
    route_to_code             
varchar
(
20)                                                                  
null comment 
'目的地行政区编码',
    route_to_name             
varchar
(
50)                                                                  
null comment 
'目的地行政区名称',
    route_current_code        
varchar
(
20)                                                                  
null comment 
'当前行政区编码',
    route_current_name        
varchar
(
50)                                                                  
null comment 
'当前行政区名称',
    estimated_arrival_time    datetime                                                                     
null comment 
'预计到达时间',
    tracking_updated_at       datetime                                                                     not 
null comment 
'最后跟踪时间',
    trail_url                 
varchar
(
255)                                                                 
null comment 
'轨迹地图URL',
    created_at                datetime                                           
default CURRENT_TIMESTAMP 
null comment 
'创建时间',
    updated_at                datetime                                           
default CURRENT_TIMESTAMP 
null on update CURRENT_TIMESTAMP comment 
'更新时间',
    created_user_id           bigint                                                                       
null comment 
'创建人ID',
    updated_user_id           bigint                                                                       
null comment 
'更新人ID',
    created_user_nickname     
varchar
(
100)                                                                 
null comment 
'创建人昵称',
    updated_user_nickname     
varchar
(
100)                                                                 
null comment 
'更新人昵称',
    deleted                   tinyint                                            
default 
0                 
null comment 
'删除标记(0:未删除,1:已删除)'
)
    comment 
'快递轨迹跟踪表' charset = utf8mb4;

create index idx_express_company
    on soms.express_tracking (express_company);

create index idx_express_no
    on soms.express_tracking (express_no);

create index idx_track_status
    on soms.express_tracking (track_status);

create 
table soms.tracking_points
(
    id                    
bigint unsigned auto_increment comment 
'主键ID'
        
primary key,
    tracking_id           
bigint unsigned                    
not 
null comment 
'关联跟踪记录ID',
    point_description     text                               
not 
null comment 
'轨迹点描述',
    point_time            datetime                           
null comment 
'轨迹点时间',
    point_status          
varchar(
50)                        
null comment 
'状态名称',
    status_code           
int                                
null comment 
'状态代码',
    area_code             
varchar(
20)                        
null comment 
'区域编码',
    area_name             
varchar(
50)                        
null comment 
'区域名称',
    created_at            datetime 
default 
CURRENT_TIMESTAMP 
null comment 
'创建时间',
    updated_at            datetime 
default 
CURRENT_TIMESTAMP 
null 
on 
update 
CURRENT_TIMESTAMP comment 
'更新时间',
    created_user_id       
bigint                             
null comment 
'创建人ID',
    updated_user_id       
bigint                             
null comment 
'更新人ID',
    created_user_nickname 
varchar(
100)                       
null comment 
'创建人昵称',
    updated_user_nickname 
varchar(
100)                       
null comment 
'更新人昵称',
    deleted               tinyint  
default 
0                 
null comment 
'删除标记(0:未删除,1:已删除)',
    
constraint tracking_points_ibfk_1
        
foreign key (tracking_id) 
references soms.express_tracking (id)
            
on 
delete cascade
)
    comment 
'快递轨迹点明细表' charset 
= utf8mb4;


create index idx_point_time
    
on soms.tracking_points (point_time);


create index idx_tracking_id
    
on soms.tracking_points (tracking_id);

create 
table soms.tracking_special_status
(
    id                    
bigint unsigned auto_increment comment 
'主键ID'
        
primary key,
    tracking_id           
bigint unsigned                                        
not 
null comment 
'关联跟踪记录ID',
    status_type           enum (
'abort-3day', 
'company-corrected', 
'suspicious') 
null comment 
'特殊状态类型',
    action_taken          enum (
'resubmitted', 
'marked-fake', 
'updated-company') 
null comment 
'已采取的行动',
    action_date           datetime                                               
null comment 
'行动日期',
    resubmit_count        tinyint  
default 
0                                     
null comment 
'重新提交次数',
    created_at            datetime 
default 
CURRENT_TIMESTAMP                     
null comment 
'创建时间',
    updated_at            datetime 
default 
CURRENT_TIMESTAMP                     
null 
on 
update 
CURRENT_TIMESTAMP comment 
'更新时间',
    created_user_id       
bigint                                                 
null comment 
'创建人ID',
    updated_user_id       
bigint                                                 
null comment 
'更新人ID',
    created_user_nickname 
varchar(
100)                                           
null comment 
'创建人昵称',
    updated_user_nickname 
varchar(
100)                                           
null comment 
'更新人昵称',
    deleted               tinyint  
default 
0                                     
null comment 
'删除标记(0:未删除,1:已删除)',
    
constraint tracking_special_status_ibfk_1
        
foreign key (tracking_id) 
references soms.express_tracking (id)
            
on 
delete cascade
)
    comment 
'特殊状态处理记录表' charset 
= utf8mb4;


create index idx_status_type
    
on soms.tracking_special_status (status_type);


create index tracking_id
    
on soms.tracking_special_status (tracking_id);

3.接口的实现

3.1订阅接口

订阅接口很简单、从表里面查出需要的数据进行组装、发送给快递100、发送形式为表单类型的数据、订阅成功之后快递100官方会调用你的回调地址把物流相关信息返回给你、可以查看官方文档。

需要注意的点:

顺丰快递需要发手机号、salt表示使用加密签名、resultv2(3、5、7返回的信息越来越详细)


package main


import (
	
"encoding/json"
	
"fmt"
	
"log"
	
"net/http"
	
"net/url"
	
"time"
)


// 快递100订阅接口配置

const (
	SubscribeURL = 
"http://poll.kuaidi100.com/pollmap"
	APIKey       = 
"YOUR_API_KEY_HERE" 
// 替换为实际API密钥
	CallbackURL  = 
"https://yourdomain.com/callback"
	Salt         = 
"your_random_salt"
	ResultV2     = 
"5" 
// 使用5获取更多物流详情
)


// 订阅请求数据结构

type SubscribeRequest 
struct {
	Key      
string                 
`json:"key"`
	Company  
string                 
`json:"company"`
	Number   
string                 
`json:"number"`
	From     
string                 
`json:"from"`
	To       
string                 
`json:"to"`
	Params   SubscribeParams        
`json:"parameters"`
}


type SubscribeParams 
struct {
	CallbackURL 
string 
`json:"callbackurl"`
	Salt        
string 
`json:"salt,omitempty"`
	Phone       
string 
`json:"phone,omitempty"` 
// 顺丰快递需要此参数
	ResultV2    
string 
`json:"resultv2,omitempty"`
}


// 订阅响应数据结构

type SubscribeResponse 
struct {
	Result     
bool   
`json:"result"`
	ReturnCode 
string 
`json:"returnCode"`
	Message    
string 
`json:"message"`
}



func 
main
() {
	
// 模拟需要订阅的订单数据
	orders := []
struct {
		ID             
int
		DeliveryCompany 
string
		DeliveryNum     
string
		FromAddress    
string
		ToAddress      
string
		Phone          
string
	}{
		{
			ID:             
1,
			DeliveryCompany: 
"yuantong",
			DeliveryNum:     
"YT1234567890",
			FromAddress:    
"上海市浦东新区",
			ToAddress:      
"北京市朝阳区",
			Phone:          
"13800138000", 
// 顺丰快递需要手机号
		},
		
// 可以添加更多订单...
	}

	
// 遍历订单进行订阅
	
for _, order := 
range orders {
		err := subscribeExpress(order)
		
if err != 
nil {
			log.Printf(
"订单 %d 订阅失败: %v", order.ID, err)
			
continue
		}
		log.Printf(
"订单 %d 订阅成功", order.ID)
	}
}


// 订阅快递单号


func 
subscribeExpress
(order 
struct {
	ID             
int
	DeliveryCompany 
string
	DeliveryNum     
string
	FromAddress    
string
	ToAddress      
string
	Phone          
string
}) 
error {
	
// 构建订阅请求
	req := SubscribeRequest{
		Key:     APIKey,
		Company: order.DeliveryCompany,
		Number:  order.DeliveryNum,
		From:    order.FromAddress,
		To:      order.ToAddress,
		Params: SubscribeParams{
			CallbackURL: CallbackURL,
			Salt:        Salt,
			Phone:       order.Phone,
			ResultV2:    ResultV2,
		},
	}

	
// 转换为JSON
	paramJSON, err := json.Marshal(req)
	
if err != 
nil {
		
return fmt.Errorf(
"JSON编码失败: %v", err)
	}

	
// 构建表单数据
	formData := url.Values{}
	formData.Set(
"schema", 
"json")
	formData.Set(
"param", 
string(paramJSON))

	
// 发送HTTP请求
	resp, err := http.PostForm(SubscribeURL, formData)
	
if err != 
nil {
		
return fmt.Errorf(
"HTTP请求失败: %v", err)
	}
	
defer resp.Body.Close()

	
// 解析响应
	
var result SubscribeResponse
	
if err := json.NewDecoder(resp.Body).Decode(&result); err != 
nil {
		
return fmt.Errorf(
"响应解析失败: %v", err)
	}

	
// 检查订阅结果
	
if result.ReturnCode != 
"200" {
		
return fmt.Errorf(
"订阅失败: %s", result.Message)
	}

	
return 
nil
}


// 注意:实际应用中应该添加重试机制和更完善的错误处理

3.2推送接口

推送接口就是快递100把物流相关信息推送过来、trailUrl就是我们需要的地图url、可以直接引入。

因为推送数据包含基础信息、和物流轨迹信息、还有一个特殊状态、所以我是设计了3个表(根据官方文档按需要设计自己的表)


package main


import (
	
"bytes"
	
"encoding/json"
	
"fmt"
	
"io"
	
"log"
	
"net/http"
	
"strings"

	
"github.com/gin-gonic/gin"
)


// 推送数据结构

type PushData 
struct {
	Sign  
string 
`json:"sign"`
	Param 
string 
`json:"param"`
}


type PushParam 
struct {
	Status    
string     
`json:"status"`
	AutoCheck 
string     
`json:"autoCheck"`
	ComOld    
string     
`json:"comOld,omitempty"`
	ComNew    
string     
`json:"comNew,omitempty"`
	LastResult LastResult 
`json:"lastResult"`
}


type LastResult 
struct {
	TrailUrl 
string 
`json:"trailUrl"`
	
// 其他字段根据需要添加
}



func 
main
() {
	r := gin.Default()
	
	
// 推送回调接口
	r.POST(
"/callback", handleCallback)
	
	log.Println(
"启动快递100推送服务,监听端口:8080")
	log.Fatal(r.Run(
":8080"))
}


// 回调处理函数


func 
handleCallback
(c *gin.Context) {
	
// 1. 读取原始请求体
	body, err := io.ReadAll(c.Request.Body)
	
if err != 
nil {
		log.Printf(
"读取请求体失败: %v\n", err)
		c.JSON(
400, gin.H{
"error": 
"读取请求体失败"})
		
return
	}
	
	
// 打印原始请求体(调试用)
	log.Printf(
"原始请求体: %s\n", 
string(body))
	
	
// 重置请求体,以便后续处理
	c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
	
	
// 2. 尝试获取sign和param参数
	sign := c.PostForm(
"sign")
	paramStr := c.PostForm(
"param")
	
	
// 3. 兼容字段名带空白符的情况
	
if paramStr == 
"" || sign == 
"" {
		
for key, values := 
range c.Request.Form {
			trimmedKey := strings.TrimSpace(key)
			
if trimmedKey == 
"param" && paramStr == 
"" && 
len(values) > 
0 {
				paramStr = values[
0]
				log.Printf(
"通过遍历Form找到param: %s\n", paramStr)
			}
			
if trimmedKey == 
"sign" && sign == 
"" && 
len(values) > 
0 {
				sign = values[
0]
				log.Printf(
"通过遍历Form找到sign: %s\n", sign)
			}
		}
	}
	
	
// 4. 尝试其他方式获取参数
	
if paramStr == 
"" {
		
// 尝试从Query参数获取
		paramStr = c.Query(
"param")
		log.Printf(
"从Query参数获取param: %s\n", paramStr)
	}
	
	
if paramStr == 
"" {
		
// 尝试从JSON body获取
		
var jsonBody 
map[
string]
interface{}
		
if err := c.ShouldBindJSON(&jsonBody); err == 
nil {
			
if param, ok := jsonBody[
"param"].(
string); ok {
				paramStr = param
				log.Printf(
"从JSON body获取param: %s\n", paramStr)
			}
		}
	}
	
	
// 5. 尝试从原始body中解析
	
if paramStr == 
"" && 
len(body) > 
0 {
		log.Printf(
"尝试从原始body解析form数据\n")
		
// 手动解析form数据
		
if err := c.Request.ParseForm(); err == 
nil {
			paramStr = c.Request.FormValue(
"param")
			sign = c.Request.FormValue(
"sign")
			log.Printf(
"从手动解析的form获取: sign=%s, param=%s\n", sign, paramStr)
		}
	}
	
	
// 6. 检查参数是否存在
	
if paramStr == 
"" {
		log.Printf(
"param参数缺失,返回错误\n")
		c.JSON(
400, gin.H{
"msg": 
"param参数缺失"})
		
return
	}
	
	
// 7. 解析param字段(JSON字符串)
	
var param PushParam
	
if err := json.Unmarshal([]
byte(paramStr), ¶m); err != 
nil {
		log.Printf(
"param解析失败: %v, paramStr: %s\n", err, paramStr)
		c.JSON(
400, gin.H{
"msg": 
"param解析失败"})
		
return
	}
	
	
// 8. 打印解析后的完整数据结构
	log.Printf(
"解析后的快递100推送数据:sign=%s, param=%+v\n", sign, param)
	
	
// 9. 业务处理(这里简化为打印和保存)
	processPushData(sign, param)
	
	
// 10. 检查地图URL是否存在
	
if param.LastResult.TrailUrl == 
"" {
		log.Println(
"trailUrl数据为空")
		c.JSON(
500, gin.H{
			
"result":     
false,
			
"returnCode": 
"500",
			
"message":    
"trailUrl数据为空",
		})
	} 
else {
		log.Println(
"处理成功")
		
// 返回成功响应
		c.JSON(
200, gin.H{
			
"result":     
true,
			
"returnCode": 
"200",
			
"message":    
"success",
		})
	}
}


// 处理推送数据


func 
processPushData
(sign 
string, param PushParam) {
	
// 这里可以:
	
// 1. 验证签名(param+Config.Salt)
	
// 2. 保存物流信息到数据库
	
// 3. 处理特殊状态(签收/中止等)
	
	log.Printf(
"收到推送: sign=%s, status=%s, trailUrl=%s", 
		sign, param.Status, param.LastResult.TrailUrl)
	
	
// 示例:打印所有数据
	log.Printf(
"完整推送数据: %+v", param)
	
	
// 公司编码纠错处理
	
if param.AutoCheck == 
"1" {
		log.Printf(
"公司编码纠错: %s -> %s", param.ComOld, param.ComNew)
	}
	
	
// 处理特殊状态
	
switch param.Status {
	
case 
"shutdown":
		log.Println(
"快递已签收")
	
case 
"abort":
		log.Println(
"快递状态中止(60天无变化/3天无记录)")
	
case 
"updateall":
		log.Println(
"重新推送所有数据")
	}
	
	
// 保存到数据库(伪代码)
	
// saveToDatabase(param)
}


// 模拟保存到数据库


func 
saveToDatabase
(param PushParam) {
	log.Printf(
"保存物流信息到数据库: 单号=%s, 状态=%s", param.LastResult.TrailUrl, param.Status)
	
// 实际实现数据库保存逻辑
}

需要注意的点:

推送接口的数据的数据不是使用的json、是表单格式、单纯请求表单请求不到、需要兼容带空格的数据

重点是下面这一段

// 兼容字段名带空白符的情况
if 
paramStr == 
"" || sign == 
"" {
    for key, values := range ctx.Request.Form {
        trimmedKey := strings.TrimSpace(key)
        if 
trimmedKey == 
"param" && paramStr == 
"" && len(values) > 
0 {
            
paramStr = values[
0]
            log.Printf("通过遍历Form找到param: %s\n", paramStr)
        }
        if 
trimmedKey == 
"sign" && sign == 
"" && len(values) > 
0 {
            
sign = values[
0]
            log.Printf("通过遍历Form找到sign: %s\n", sign)
        }
    }
}

3.3获取物流信息

这个就很简单了、在单据上面加一个按钮、点击按钮获取物流信息跟物流轨迹、地图直接嵌入trailurl(都是从表里面拿的)在页面上显示

	
// 1. 查询所有与商城订单ID相关的出库单
	param := &repo.SimsEnterLeaveInventoryBillDBDataParam{
		SimsEnterLeaveInventoryBillDBData: repo.SimsEnterLeaveInventoryBillDBData{
			Type:        
"6",
			RelevanceId: orderId,
		},
	}
	dbList, count, err := s.billRepo.List(ctx, 
"", 
0, 
0, param)
	
if err != 
nil {
		
return 
nil, err
	}
	resp := &api.BatchGetExpressTrackingResponse{
		Code:    
"200",
		Message: 
"success",
	}

	
if dbList == 
nil || 
len(*dbList) == 
0 {
		log.Printf(
"GetExpressTrackingByOrderId: 未查到出库单")
		resp.Code = 
"404"
		resp.Message = 
"未查到出库单"
		resp.Data = 
nil
		
return resp, 
nil
	}

	
var data []*api.ExpressTrackingInfo
	
var failedNos []
string
	
var successCount, failureCount 
int32

	
for _, dbData := 
range *dbList {
		
if dbData.DeliveryNum == 
"" {
			log.Printf(
"GetExpressTrackingByOrderId: 出库单ID=%d 未填写快递单号", dbData.Id)
			failedNos = 
append(failedNos, 
"")
			failureCount++
			
continue
		}
		
// 2. 根据快递单号获取物流信息
		expressData, err := s.trackingRepo.GetByExpressNo(ctx, dbData.DeliveryNum)
		
if err != 
nil || expressData == 
nil {
			log.Printf(
"GetExpressTrackingByOrderId: 出库单ID=%d 未查到物流信息", dbData.Id)
			failedNos = 
append(failedNos, dbData.DeliveryNum)
			failureCount++
			
continue
		}
		
// 3. 组装数据
		pointsData, _ := s.pointsRepo.GetByTrackingID(ctx, expressData.Id)
		specialData, _ := s.specialRepo.GetByTrackingID(ctx, expressData.Id)
		info := stru.ConvertToExpressTrackingInfo(expressData, pointsData, specialData)
		data = 
append(data, info)
		successCount++
	}

	resp.Data = data
	resp.SuccessCount = successCount
	resp.FailureCount = failureCount
	resp.FailedExpressNos = failedNos
	resp.ResponseAt = time.Now().Format(
"2006-01-02 15:04:05")
	resp.ProcessingTimeMs = 
0 
// 可选

	
if 
len(data) == 
0 {
		resp.Code = 
"404"
		resp.Message = 
"未查到任何物流信息"
	}

	
return resp, 
nil
}

<"calcifer.h8m6.cn"><"monster.h8m6.cn"><"mike.h8m6.cn">

请使用浏览器的分享功能分享到微信等