新增
This commit is contained in:
@@ -0,0 +1,540 @@
|
||||
# 物流追踪系统完整方案
|
||||
|
||||
## 系统架构
|
||||
|
||||
### 核心功能
|
||||
|
||||
1. **物流信息存储** - 将快递100查询结果存储到数据库
|
||||
2. **自动更新机制** - 定时自动查询更新物流状态
|
||||
3. **状态变更检测** - 监控物流状态变化
|
||||
4. **异常通知** - 签收、异常时自动通知
|
||||
5. **查询日志** - 记录所有查询请求和响应
|
||||
|
||||
### 数据库表结构
|
||||
|
||||
| 表名 | 说明 | 用途 |
|
||||
|------|------|------|
|
||||
| zyt_express_tracking | 物流追踪主表 | 存储快递单基本信息和当前状态 |
|
||||
| zyt_express_trace | 物流轨迹明细表 | 存储每条物流轨迹记录 |
|
||||
| zyt_express_state_log | 状态变更记录表 | 记录状态变化历史 |
|
||||
| zyt_express_query_log | 查询日志表 | 记录所有查询请求 |
|
||||
|
||||
---
|
||||
|
||||
## 安装步骤
|
||||
|
||||
### 1. 创建数据库表
|
||||
|
||||
```bash
|
||||
# 执行SQL脚本
|
||||
mysql -u root -p your_database < server/database/migrations/create_express_tracking_tables.sql
|
||||
```
|
||||
|
||||
或在数据库管理工具中执行 `create_express_tracking_tables.sql` 文件。
|
||||
|
||||
### 2. 注册定时任务命令
|
||||
|
||||
在 `server/config/console.php` 中添加:
|
||||
|
||||
```php
|
||||
'commands' => [
|
||||
// ... 其他命令
|
||||
'express:auto-update' => \app\command\ExpressAutoUpdate::class,
|
||||
],
|
||||
```
|
||||
|
||||
### 3. 配置crontab定时任务
|
||||
|
||||
```bash
|
||||
# 编辑crontab
|
||||
crontab -e
|
||||
|
||||
# 添加以下行(每10分钟执行一次)
|
||||
*/10 * * * * cd /path/to/your/server && php think express:auto-update >> /dev/null 2>&1
|
||||
```
|
||||
|
||||
### 4. 测试定时任务
|
||||
|
||||
```bash
|
||||
cd server
|
||||
php think express:auto-update
|
||||
```
|
||||
|
||||
应该看到输出:
|
||||
```
|
||||
开始自动更新物流信息...
|
||||
更新完成!
|
||||
总数: 0
|
||||
成功: 0
|
||||
失败: 0
|
||||
耗时: 0.05秒
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 创建物流追踪
|
||||
|
||||
当订单发货时,创建物流追踪记录:
|
||||
|
||||
```php
|
||||
use app\common\service\ExpressTrackingService;
|
||||
|
||||
// 创建追踪记录
|
||||
$tracking = ExpressTrackingService::createOrUpdate([
|
||||
'order_id' => 123, // 订单ID
|
||||
'order_type' => 'prescription', // 订单类型
|
||||
'tracking_number' => 'JD0230761381812', // 快递单号
|
||||
'express_company' => 'jd', // 快递公司
|
||||
'recipient_phone' => '13800138000', // 收件人手机
|
||||
'recipient_name' => '张三', // 收件人姓名
|
||||
'recipient_address' => '北京市朝阳区xxx', // 收件地址
|
||||
]);
|
||||
|
||||
// 系统会立即查询一次物流信息并存储
|
||||
```
|
||||
|
||||
### 2. 手动更新物流信息
|
||||
|
||||
```php
|
||||
// 更新指定追踪记录
|
||||
$result = ExpressTrackingService::queryAndUpdate($trackingId, false);
|
||||
```
|
||||
|
||||
### 3. 获取物流详情
|
||||
|
||||
```php
|
||||
// 获取完整的物流信息(包含轨迹)
|
||||
$detail = ExpressTrackingService::getDetail($trackingId);
|
||||
|
||||
// 返回数据包含:
|
||||
// - 基本信息
|
||||
// - 当前状态
|
||||
// - 所有轨迹记录
|
||||
// - 状态变更历史
|
||||
```
|
||||
|
||||
### 4. 自动更新配置
|
||||
|
||||
```php
|
||||
// 修改更新间隔(秒)
|
||||
$tracking->update_interval = 1800; // 30分钟
|
||||
$tracking->save();
|
||||
|
||||
// 停止自动更新
|
||||
$tracking->auto_update = 0;
|
||||
$tracking->save();
|
||||
|
||||
// 启用自动更新
|
||||
$tracking->auto_update = 1;
|
||||
$tracking->next_update_time = time(); // 立即更新
|
||||
$tracking->save();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 集成到现有订单系统
|
||||
|
||||
### 修改订单创建/编辑逻辑
|
||||
|
||||
在 `PrescriptionOrderLogic.php` 中:
|
||||
|
||||
```php
|
||||
public static function create(array $params, int $adminId, array $adminInfo)
|
||||
{
|
||||
// ... 原有创建逻辑
|
||||
|
||||
$order->save();
|
||||
|
||||
// 创建物流追踪
|
||||
if (!empty($params['tracking_number'])) {
|
||||
ExpressTrackingService::createOrUpdate([
|
||||
'order_id' => $order->id,
|
||||
'order_type' => 'prescription',
|
||||
'tracking_number' => $params['tracking_number'],
|
||||
'express_company' => $params['express_company'] ?? 'auto',
|
||||
'recipient_phone' => $order->recipient_phone,
|
||||
'recipient_name' => $order->recipient_name,
|
||||
'recipient_address' => $order->shipping_address,
|
||||
]);
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function edit(array $params, int $adminId, array $adminInfo)
|
||||
{
|
||||
// ... 原有编辑逻辑
|
||||
|
||||
// 更新物流追踪
|
||||
if (array_key_exists('tracking_number', $params)) {
|
||||
if (!empty($params['tracking_number'])) {
|
||||
ExpressTrackingService::createOrUpdate([
|
||||
'order_id' => $order->id,
|
||||
'order_type' => 'prescription',
|
||||
'tracking_number' => $params['tracking_number'],
|
||||
'express_company' => $order->express_company,
|
||||
'recipient_phone' => $order->recipient_phone,
|
||||
'recipient_name' => $order->recipient_name,
|
||||
'recipient_address' => $order->shipping_address,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
```
|
||||
|
||||
### 修改物流查询接口
|
||||
|
||||
在 `PrescriptionOrderLogic.php` 中:
|
||||
|
||||
```php
|
||||
public static function logisticsTrace(int $id, string $expressCompanyOverride, int $adminId, array $adminInfo): ?array
|
||||
{
|
||||
// ... 原有逻辑
|
||||
|
||||
// 先从数据库获取
|
||||
$tracking = ExpressTracking::where('order_id', $id)
|
||||
->where('order_type', 'prescription')
|
||||
->whereNull('delete_time')
|
||||
->find();
|
||||
|
||||
if ($tracking) {
|
||||
// 如果距离上次查询超过5分钟,重新查询
|
||||
if (time() - $tracking->last_query_time > 300) {
|
||||
ExpressTrackingService::queryAndUpdate($tracking->id, false);
|
||||
$tracking->refresh();
|
||||
}
|
||||
|
||||
// 返回数据库中的数据
|
||||
$payload = [
|
||||
'carrier' => $tracking->express_company,
|
||||
'carrier_label' => $tracking->express_company_name,
|
||||
'kuaidi_com' => $tracking->express_company,
|
||||
'traces' => $tracking->traces->map(function($trace) {
|
||||
return [
|
||||
'time' => $trace->trace_time,
|
||||
'context' => $trace->trace_context,
|
||||
'status' => $trace->status,
|
||||
'location' => $trace->location,
|
||||
];
|
||||
})->toArray(),
|
||||
'state' => $tracking->current_state,
|
||||
'state_text' => $tracking->current_state_text,
|
||||
'source' => $tracking->data_source,
|
||||
'hint' => '',
|
||||
'official_url' => ExpressTrackService::officialUrls($tracking->tracking_number)[$tracking->express_company] ?? '',
|
||||
'official_urls' => ExpressTrackService::officialUrls($tracking->tracking_number),
|
||||
'tracking_number' => $tracking->tracking_number,
|
||||
'express_company_used' => $tracking->express_company,
|
||||
'is_signed' => $tracking->is_signed,
|
||||
'sign_time' => $tracking->sign_time,
|
||||
'estimated_arrival_time' => $tracking->estimated_arrival_time,
|
||||
];
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
// 如果数据库中没有,走原有逻辑
|
||||
// ... 原有查询逻辑
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自动更新机制
|
||||
|
||||
### 更新策略
|
||||
|
||||
1. **创建时立即查询** - 创建追踪记录时立即查询一次
|
||||
2. **定时自动更新** - 每10分钟检查一次需要更新的记录
|
||||
3. **智能更新间隔** - 默认30分钟更新一次
|
||||
4. **终态停止更新** - 签收、退签、拒签后停止自动更新
|
||||
|
||||
### 更新间隔建议
|
||||
|
||||
| 物流状态 | 更新间隔 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 已下单/待揽收 | 30分钟 | 等待揽收 |
|
||||
| 已揽收/在途 | 30分钟 | 运输中 |
|
||||
| 派件中 | 15分钟 | 即将签收,加快更新 |
|
||||
| 已签收 | 停止 | 终态,无需更新 |
|
||||
| 疑难/异常 | 60分钟 | 降低频率 |
|
||||
|
||||
### 定时任务配置
|
||||
|
||||
```bash
|
||||
# 每10分钟执行一次(推荐)
|
||||
*/10 * * * * cd /path/to/server && php think express:auto-update
|
||||
|
||||
# 每5分钟执行一次(高频)
|
||||
*/5 * * * * cd /path/to/server && php think express:auto-update
|
||||
|
||||
# 每30分钟执行一次(低频)
|
||||
*/30 * * * * cd /path/to/server && php think express:auto-update
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 状态变更通知
|
||||
|
||||
### 通知触发条件
|
||||
|
||||
1. **签收通知** - 快递签收时通知
|
||||
2. **异常通知** - 出现疑难、拒签等异常时通知
|
||||
3. **状态变更** - 每次状态变化都会记录
|
||||
|
||||
### 通知渠道(可扩展)
|
||||
|
||||
#### 1. 企业微信通知
|
||||
|
||||
```php
|
||||
// 在 ExpressTrackingService::sendNotification() 中实现
|
||||
use app\common\service\WechatWorkService;
|
||||
|
||||
WechatWorkService::sendMessage([
|
||||
'touser' => $userId,
|
||||
'msgtype' => 'text',
|
||||
'text' => [
|
||||
'content' => "【物流通知】\n" .
|
||||
"快递单号:{$tracking->tracking_number}\n" .
|
||||
"状态变更:{$log->old_state_text} → {$log->new_state_text}\n" .
|
||||
"最新动态:{$log->change_reason}"
|
||||
]
|
||||
]);
|
||||
```
|
||||
|
||||
#### 2. 短信通知
|
||||
|
||||
```php
|
||||
// 对接短信服务商
|
||||
SmsService::send($tracking->recipient_phone, [
|
||||
'template' => 'express_status_change',
|
||||
'params' => [
|
||||
'tracking_number' => $tracking->tracking_number,
|
||||
'status' => $log->new_state_text,
|
||||
]
|
||||
]);
|
||||
```
|
||||
|
||||
#### 3. 系统内通知
|
||||
|
||||
```php
|
||||
// 创建系统通知记录
|
||||
NoticeService::create([
|
||||
'user_id' => $userId,
|
||||
'title' => '物流状态更新',
|
||||
'content' => "您的快递 {$tracking->tracking_number} 已{$log->new_state_text}",
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据统计和分析
|
||||
|
||||
### 查询统计
|
||||
|
||||
```sql
|
||||
-- 查询成功率
|
||||
SELECT
|
||||
DATE(FROM_UNIXTIME(query_time)) as date,
|
||||
COUNT(*) as total,
|
||||
SUM(is_success) as success,
|
||||
ROUND(SUM(is_success) / COUNT(*) * 100, 2) as success_rate
|
||||
FROM zyt_express_query_log
|
||||
GROUP BY date
|
||||
ORDER BY date DESC;
|
||||
|
||||
-- 平均响应时间
|
||||
SELECT
|
||||
query_source,
|
||||
AVG(response_time) as avg_response_time,
|
||||
MAX(response_time) as max_response_time
|
||||
FROM zyt_express_query_log
|
||||
WHERE is_success = 1
|
||||
GROUP BY query_source;
|
||||
```
|
||||
|
||||
### 物流状态分布
|
||||
|
||||
```sql
|
||||
-- 当前物流状态分布
|
||||
SELECT
|
||||
current_state,
|
||||
current_state_text,
|
||||
COUNT(*) as count
|
||||
FROM zyt_express_tracking
|
||||
WHERE delete_time IS NULL
|
||||
GROUP BY current_state, current_state_text
|
||||
ORDER BY count DESC;
|
||||
|
||||
-- 签收率统计
|
||||
SELECT
|
||||
DATE(FROM_UNIXTIME(sign_time)) as date,
|
||||
COUNT(*) as signed_count
|
||||
FROM zyt_express_tracking
|
||||
WHERE is_signed = 1
|
||||
GROUP BY date
|
||||
ORDER BY date DESC;
|
||||
```
|
||||
|
||||
### 异常统计
|
||||
|
||||
```sql
|
||||
-- 异常快递统计
|
||||
SELECT
|
||||
tracking_number,
|
||||
express_company_name,
|
||||
current_state_text,
|
||||
latest_trace_context,
|
||||
FROM_UNIXTIME(update_time) as update_time
|
||||
FROM zyt_express_tracking
|
||||
WHERE current_state IN ('2', '13', '14') -- 疑难、清关异常、拒签
|
||||
AND delete_time IS NULL
|
||||
ORDER BY update_time DESC;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 批量更新优化
|
||||
|
||||
```php
|
||||
// 每次处理50条记录
|
||||
ExpressTrackingService::autoUpdateBatch(50);
|
||||
|
||||
// 根据服务器性能调整
|
||||
// - 性能好:100条
|
||||
// - 性能一般:50条
|
||||
// - 性能差:20条
|
||||
```
|
||||
|
||||
### 2. 查询频率控制
|
||||
|
||||
```php
|
||||
// 避免频繁查询同一单号
|
||||
if (time() - $tracking->last_query_time < 300) {
|
||||
// 5分钟内不重复查询
|
||||
return $cachedResult;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 索引优化
|
||||
|
||||
数据库表已创建必要索引:
|
||||
- `idx_order_id` - 订单ID索引
|
||||
- `idx_auto_update` - 自动更新索引
|
||||
- `idx_tracking_number` - 快递单号索引
|
||||
|
||||
### 4. 数据清理
|
||||
|
||||
```sql
|
||||
-- 清理90天前的查询日志
|
||||
DELETE FROM zyt_express_query_log
|
||||
WHERE create_time < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 90 DAY));
|
||||
|
||||
-- 清理已签收超过30天的轨迹明细
|
||||
DELETE t FROM zyt_express_trace t
|
||||
INNER JOIN zyt_express_tracking tr ON t.tracking_id = tr.id
|
||||
WHERE tr.is_signed = 1
|
||||
AND tr.sign_time < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 监控和告警
|
||||
|
||||
### 1. 监控指标
|
||||
|
||||
- 自动更新成功率
|
||||
- 平均响应时间
|
||||
- 异常快递数量
|
||||
- 查询失败率
|
||||
|
||||
### 2. 告警规则
|
||||
|
||||
```php
|
||||
// 在定时任务中添加告警逻辑
|
||||
$result = ExpressTrackingService::autoUpdateBatch(50);
|
||||
|
||||
if ($result['failed'] > $result['success'] * 0.5) {
|
||||
// 失败率超过50%,发送告警
|
||||
AlertService::send('物流自动更新失败率过高', $result);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 定时任务不执行?
|
||||
|
||||
**检查步骤:**
|
||||
1. 确认crontab已配置:`crontab -l`
|
||||
2. 检查PHP路径:`which php`
|
||||
3. 检查项目路径是否正确
|
||||
4. 查看cron日志:`tail -f /var/log/cron`
|
||||
|
||||
### Q2: 更新频率太高,快递100余额消耗快?
|
||||
|
||||
**解决方案:**
|
||||
1. 增加更新间隔:`update_interval = 3600`(1小时)
|
||||
2. 减少批量处理数量:`autoUpdateBatch(20)`
|
||||
3. 签收后立即停止更新
|
||||
4. 使用智能更新策略
|
||||
|
||||
### Q3: 如何查看某个快递的完整历史?
|
||||
|
||||
```php
|
||||
$detail = ExpressTrackingService::getDetail($trackingId);
|
||||
|
||||
// 包含:
|
||||
// - traces: 所有轨迹记录
|
||||
// - state_logs: 状态变更历史
|
||||
```
|
||||
|
||||
### Q4: 如何手动触发更新?
|
||||
|
||||
```php
|
||||
// 方法1:通过服务类
|
||||
ExpressTrackingService::queryAndUpdate($trackingId, false);
|
||||
|
||||
// 方法2:通过命令行
|
||||
php think express:auto-update
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
### 优势
|
||||
|
||||
1. **数据持久化** - 所有物流信息存储在数据库,随时查询
|
||||
2. **自动更新** - 无需手动查询,系统自动追踪
|
||||
3. **状态监控** - 实时监控物流状态变化
|
||||
4. **异常通知** - 及时发现和处理异常
|
||||
5. **数据分析** - 支持物流数据统计分析
|
||||
|
||||
### 成本
|
||||
|
||||
1. **快递100费用** - 按查询次数计费
|
||||
2. **服务器资源** - 定时任务占用少量CPU和内存
|
||||
3. **数据库存储** - 需要额外的存储空间
|
||||
|
||||
### 建议
|
||||
|
||||
1. **充值快递100** - 确保有足够余额
|
||||
2. **合理设置更新间隔** - 平衡实时性和成本
|
||||
3. **定期清理数据** - 避免数据库膨胀
|
||||
4. **监控运行状态** - 及时发现问题
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- `LOGISTICS_INTEGRATION_GUIDE.md` - 物流集成指南
|
||||
- `KUAIDI100_ACCOUNT_ISSUE.md` - 快递100账号问题
|
||||
- `JTEXPRESS_SUPPORT_ADDED.md` - 极兔速递支持
|
||||
Reference in New Issue
Block a user