# 物流追踪系统完整方案 ## 系统架构 ### 核心功能 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` - 极兔速递支持