소스 검색

不打印,任务处理bug

lizhen 14 시간 전
부모
커밋
f79f92b8c3
5개의 변경된 파일544개의 추가작업 그리고 3개의 파일을 삭제
  1. 42 1
      addons/exam/controller/Paper.php
  2. 22 2
      application/command/IcbcQueue.php
  3. 158 0
      test_icbc_queue.php
  4. 41 0
      修复卡住的队列任务.sql
  5. 281 0
      队列返回false问题修复说明.md

+ 42 - 1
addons/exam/controller/Paper.php

@@ -349,7 +349,27 @@ class Paper extends Base
         ], JSON_UNESCAPED_UNICODE), 'info');
 
         try {
+            // 检查必要的类和函数
+            if (!class_exists('\DefaultIcbcClient')) {
+                \think\Log::record('DefaultIcbcClient 类不存在', 'error');
+                return [
+                    'return_code' => '-98',
+                    'return_msg' => 'DefaultIcbcClient 类不存在',
+                    'raw_response' => ''
+                ];
+            }
+            
+            if (!function_exists('createUniqueNo')) {
+                \think\Log::record('createUniqueNo 函数不存在', 'error');
+                return [
+                    'return_code' => '-97',
+                    'return_msg' => 'createUniqueNo 函数不存在',
+                    'raw_response' => ''
+                ];
+            }
+            
             // 创建工行客户端
+            \think\Log::record('准备创建 DefaultIcbcClient...', 'info');
             $client = new \DefaultIcbcClient(
                 $data['app_id'],
                 $private_key,
@@ -359,6 +379,7 @@ class Paper extends Base
                 $public_key,
                 '', '', '', ''
             );
+            \think\Log::record('DefaultIcbcClient 创建成功', 'info');
 
             // 构建请求参数
             $request = [
@@ -514,6 +535,24 @@ class Paper extends Base
                     [$task['nickname']]
                 );
                 
+                // 先检查返回值是否为数组
+                if (!is_array($result)) {
+                    \think\Log::record('工行接口返回值异常[ID:'.$task['id'].']: ' . var_export($result, true), 'error');
+                    
+                    // 返回值异常,标记为失败
+                    $retry_count = $task['retry_count'] + 1;
+                    $status = $retry_count >= 3 ? 3 : 0;
+                    
+                    Db::name('icbc_queue')->where('id', $task['id'])->update([
+                        'status' => $status,
+                        'retry_count' => $retry_count,
+                        'result' => json_encode(['error' => '返回值不是数组', 'raw' => var_export($result, true)], JSON_UNESCAPED_UNICODE),
+                        'error_msg' => '接口返回值格式错误',
+                        'updatetime' => time()
+                    ]);
+                    continue;
+                }
+                
                 // 根据结果更新状态
                 if (isset($result['return_code']) && $result['return_code'] == 0) {
                     // 成功
@@ -527,11 +566,13 @@ class Paper extends Base
                     $retry_count = $task['retry_count'] + 1;
                     $status = $retry_count >= 3 ? 3 : 0; // 重试3次后标记为失败
                     
+                    $error_msg = isset($result['return_msg']) ? $result['return_msg'] : '未知错误';
+                    
                     Db::name('icbc_queue')->where('id', $task['id'])->update([
                         'status' => $status,
                         'retry_count' => $retry_count,
                         'result' => json_encode($result, JSON_UNESCAPED_UNICODE),
-                        'error_msg' => isset($result['return_msg']) ? $result['return_msg'] : '未知错误',
+                        'error_msg' => $error_msg,
                         'updatetime' => time()
                     ]);
                 }

+ 22 - 2
application/command/IcbcQueue.php

@@ -50,6 +50,24 @@ class IcbcQueue extends Command
                 // 调用工行接口
                 $result = $this->callIcbcApi($task);
                 
+                // 先检查返回值是否为数组
+                if (!is_array($result)) {
+                    $output->writeln("✗ 任务 {$task['id']} 返回值异常: " . var_export($result, true));
+                    
+                    // 返回值异常,标记为失败
+                    $retry_count = $task['retry_count'] + 1;
+                    $status = $retry_count >= 3 ? 3 : 0;
+                    
+                    Db::name('icbc_queue')->where('id', $task['id'])->update([
+                        'status' => $status,
+                        'retry_count' => $retry_count,
+                        'result' => json_encode(['error' => '返回值不是数组', 'raw' => var_export($result, true)], JSON_UNESCAPED_UNICODE),
+                        'error_msg' => '接口返回值格式错误',
+                        'updatetime' => time()
+                    ]);
+                    continue;
+                }
+                
                 // 根据结果更新状态
                 if (isset($result['return_code']) && $result['return_code'] == 0) {
                     // 成功
@@ -64,15 +82,17 @@ class IcbcQueue extends Command
                     $retry_count = $task['retry_count'] + 1;
                     $status = $retry_count >= 3 ? 3 : 0;
                     
+                    $error_msg = isset($result['return_msg']) ? $result['return_msg'] : '未知错误';
+                    
                     Db::name('icbc_queue')->where('id', $task['id'])->update([
                         'status' => $status,
                         'retry_count' => $retry_count,
                         'result' => json_encode($result, JSON_UNESCAPED_UNICODE),
-                        'error_msg' => isset($result['return_msg']) ? $result['return_msg'] : '未知错误',
+                        'error_msg' => $error_msg,
                         'updatetime' => time()
                     ]);
                     
-                    $output->writeln("✗ 任务 {$task['id']} 处理失败: " . (isset($result['return_msg']) ? $result['return_msg'] : '未知错误'));
+                    $output->writeln("✗ 任务 {$task['id']} 处理失败: " . $error_msg);
                 }
                 
             } catch (\Exception $e) {

+ 158 - 0
test_icbc_queue.php

@@ -0,0 +1,158 @@
+#!/usr/bin/env php
+<?php
+/**
+ * 测试和修复工行队列脚本
+ * 使用: php test_icbc_queue.php
+ */
+
+// 定义项目路径
+define('APP_PATH', __DIR__ . '/application/');
+define('EXTEND_PATH', __DIR__ . '/extend/');
+
+// 加载框架
+require __DIR__ . '/thinkphp/base.php';
+
+// 输出帮助
+echo "==========================================\n";
+echo "工行队列测试和修复工具\n";
+echo "==========================================\n\n";
+
+// 测试数据库连接
+echo "[1] 测试数据库连接...\n";
+try {
+    \think\Db::query('SELECT 1');
+    echo "✓ 数据库连接成功\n\n";
+} catch (\Exception $e) {
+    echo "✗ 数据库连接失败: " . $e->getMessage() . "\n\n";
+    exit(1);
+}
+
+// 检查队列表
+echo "[2] 检查队列表...\n";
+try {
+    $count = \think\Db::name('icbc_queue')->count();
+    echo "✓ 队列表存在,当前有 {$count} 条记录\n\n";
+} catch (\Exception $e) {
+    echo "✗ 队列表不存在或有错误: " . $e->getMessage() . "\n\n";
+    exit(1);
+}
+
+// 查看各状态任务数量
+echo "[3] 查看任务状态分布...\n";
+$statusMap = [
+    0 => '待处理',
+    1 => '处理中',
+    2 => '成功',
+    3 => '失败'
+];
+
+foreach ($statusMap as $status => $name) {
+    $count = \think\Db::name('icbc_queue')->where('status', $status)->count();
+    echo "  {$name}(status={$status}): {$count} 条\n";
+}
+echo "\n";
+
+// 查看卡住的任务
+echo "[4] 查看卡在'处理中'超过5分钟的任务...\n";
+$stuckTasks = \think\Db::name('icbc_queue')
+    ->where('status', 1)
+    ->where('updatetime', '<', time() - 300)
+    ->select();
+
+if (count($stuckTasks) > 0) {
+    echo "  发现 " . count($stuckTasks) . " 个卡住的任务:\n";
+    foreach ($stuckTasks as $task) {
+        echo "    ID: {$task['id']}, 手机号: {$task['mobile_phone']}, ";
+        echo "更新时间: " . date('Y-m-d H:i:s', $task['updatetime']) . "\n";
+    }
+    
+    // 询问是否修复
+    echo "\n  是否将这些任务重置为待处理状态? (y/n): ";
+    $handle = fopen("php://stdin", "r");
+    $line = fgets($handle);
+    if(trim($line) == 'y' || trim($line) == 'Y') {
+        $affected = \think\Db::name('icbc_queue')
+            ->where('status', 1)
+            ->where('updatetime', '<', time() - 300)
+            ->update([
+                'status' => 0,
+                'error_msg' => '任务处理超时,已重置为待处理 - ' . date('Y-m-d H:i:s')
+            ]);
+        echo "  ✓ 已重置 {$affected} 个任务\n";
+    }
+    fclose($handle);
+} else {
+    echo "  ✓ 没有卡住的任务\n";
+}
+echo "\n";
+
+// 检查必要的类和函数
+echo "[5] 检查依赖...\n";
+include_once EXTEND_PATH . 'icbc/DefaultIcbcClient.php';
+if (class_exists('\DefaultIcbcClient')) {
+    echo "  ✓ DefaultIcbcClient 类存在\n";
+} else {
+    echo "  ✗ DefaultIcbcClient 类不存在\n";
+}
+
+if (function_exists('createUniqueNo')) {
+    echo "  ✓ createUniqueNo 函数存在\n";
+    $testId = createUniqueNo('test', time());
+    echo "    测试生成: {$testId}\n";
+} else {
+    echo "  ✗ createUniqueNo 函数不存在\n";
+}
+echo "\n";
+
+// 显示最近的失败任务
+echo "[6] 显示最近5条失败的任务...\n";
+$failedTasks = \think\Db::name('icbc_queue')
+    ->where('status', 3)
+    ->order('id', 'desc')
+    ->limit(5)
+    ->select();
+
+if (count($failedTasks) > 0) {
+    foreach ($failedTasks as $task) {
+        echo "  ID: {$task['id']}\n";
+        echo "    手机号: {$task['mobile_phone']}\n";
+        echo "    积分: {$task['integral_value']}\n";
+        echo "    重试次数: {$task['retry_count']}\n";
+        echo "    错误信息: {$task['error_msg']}\n";
+        echo "    ---\n";
+    }
+} else {
+    echo "  ✓ 没有失败的任务\n";
+}
+echo "\n";
+
+// 测试调用
+echo "[7] 是否测试调用工行接口? (y/n): ";
+$handle = fopen("php://stdin", "r");
+$line = fgets($handle);
+if(trim($line) == 'y' || trim($line) == 'Y') {
+    echo "  执行测试调用...\n";
+    $testPhone = '15388010006';
+    $testScore = 1;
+    
+    // 插入测试任务
+    $testId = \think\Db::name('icbc_queue')->insertGetId([
+        'mobile_phone' => $testPhone,
+        'integral_value' => $testScore,
+        'integral_type' => '10938',
+        'nickname' => '测试用户',
+        'status' => 0,
+        'retry_count' => 0,
+        'createtime' => time(),
+        'updatetime' => time(),
+    ]);
+    
+    echo "  已创建测试任务 ID: {$testId}\n";
+    echo "  请运行: php think icbc_queue\n";
+}
+fclose($handle);
+
+echo "\n==========================================\n";
+echo "测试完成\n";
+echo "==========================================\n";
+

+ 41 - 0
修复卡住的队列任务.sql

@@ -0,0 +1,41 @@
+-- 修复卡在"处理中"状态的任务
+-- 这些任务可能因为异常导致status=1但没有继续处理
+
+-- 1. 查看所有卡住的任务(处理中且超过5分钟的)
+SELECT id, mobile_phone, integral_value, status, retry_count, 
+       FROM_UNIXTIME(createtime) as create_time,
+       FROM_UNIXTIME(updatetime) as update_time,
+       error_msg
+FROM icbc_queue 
+WHERE status = 1 
+  AND updatetime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 5 MINUTE))
+ORDER BY id DESC;
+
+-- 2. 将这些卡住的任务重置为待处理状态
+UPDATE icbc_queue 
+SET status = 0,
+    error_msg = '任务处理超时,已重置为待处理'
+WHERE status = 1 
+  AND updatetime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 5 MINUTE));
+
+-- 3. 查看所有待处理的任务
+SELECT id, mobile_phone, integral_value, status, retry_count, 
+       FROM_UNIXTIME(createtime) as create_time,
+       error_msg
+FROM icbc_queue 
+WHERE status = 0
+ORDER BY id DESC
+LIMIT 20;
+
+-- 4. 查看所有失败的任务(重试3次后仍失败)
+SELECT id, mobile_phone, integral_value, status, retry_count, 
+       FROM_UNIXTIME(createtime) as create_time,
+       error_msg, result
+FROM icbc_queue 
+WHERE status = 3
+ORDER BY id DESC
+LIMIT 20;
+
+-- 5. 如果要重试失败的任务,将status=3改为0
+-- UPDATE icbc_queue SET status = 0, retry_count = 0 WHERE status = 3;
+

+ 281 - 0
队列返回false问题修复说明.md

@@ -0,0 +1,281 @@
+# 队列返回false问题修复说明
+
+## 📋 问题描述
+
+1. **手动执行定时任务返回 `false`**
+2. **数据库 status 被更新为 1(处理中)**
+3. **第二次执行找不到待处理任务**
+4. **没有日志文件输出**
+
+## ✅ 已完成的修复
+
+### 1. 增强返回值检查(IcbcQueue.php + Paper.php)
+
+**问题**: 代码假设返回值一定是数组,但实际可能返回 `false`
+
+**修复**: 添加类型检查
+```php
+// 先检查返回值是否为数组
+if (!is_array($result)) {
+    $output->writeln("✗ 任务 {$task['id']} 返回值异常");
+    
+    // 返回值异常,标记为失败
+    $retry_count = $task['retry_count'] + 1;
+    $status = $retry_count >= 3 ? 3 : 0;
+    
+    Db::name('icbc_queue')->update([
+        'status' => $status,
+        'retry_count' => $retry_count,
+        'error_msg' => '接口返回值格式错误'
+    ]);
+    continue; // 继续处理下一个任务
+}
+```
+
+### 2. 增强错误检测(Paper.php)
+
+**新增检查**:
+- ✅ 检查 `DefaultIcbcClient` 类是否存在
+- ✅ 检查 `createUniqueNo` 函数是否存在
+- ✅ 记录客户端创建过程
+- ✅ 所有步骤都添加日志
+
+```php
+// 检查必要的类和函数
+if (!class_exists('\DefaultIcbcClient')) {
+    \think\Log::record('DefaultIcbcClient 类不存在', 'error');
+    return ['return_code' => '-98', ...];
+}
+
+if (!function_exists('createUniqueNo')) {
+    \think\Log::record('createUniqueNo 函数不存在', 'error');
+    return ['return_code' => '-97', ...];
+}
+```
+
+## 🛠️ 立即修复步骤
+
+### 步骤1: 修复当前卡住的任务
+
+执行SQL修复脚本:
+
+```bash
+# 在数据库中执行
+mysql -u用户名 -p数据库名 < 修复卡住的队列任务.sql
+```
+
+或者手动执行:
+
+```sql
+-- 查看卡住的任务
+SELECT * FROM icbc_queue WHERE status = 1;
+
+-- 重置卡住的任务为待处理
+UPDATE icbc_queue 
+SET status = 0, 
+    error_msg = '任务处理超时,已重置为待处理'
+WHERE status = 1;
+```
+
+### 步骤2: 运行测试脚本
+
+```bash
+php test_icbc_queue.php
+```
+
+这个脚本会:
+- ✅ 测试数据库连接
+- ✅ 检查队列表
+- ✅ 显示各状态任务数量
+- ✅ 查找并修复卡住的任务
+- ✅ 检查依赖
+- ✅ 显示失败任务详情
+- ✅ 可选:创建测试任务
+
+### 步骤3: 再次执行队列命令
+
+```bash
+php think icbc_queue
+```
+
+现在应该能看到详细的输出和日志了。
+
+## 📝 检查日志
+
+### 查看日志文件
+
+```bash
+# ThinkPHP 日志
+tail -f runtime/log/$(date +%y%m%d).log
+
+# 工行接口日志
+tail -f runtime/icbc_log/failicbc.txt
+tail -f runtime/icbc_log/suc.txt
+```
+
+### 预期看到的日志
+
+**正常情况**:
+```
+开始处理工行积分队列...
+找到 1 个待处理任务
+处理任务 ID: 1, 手机号: 15388010006, 积分: 10
+积分维护接口请求参数: {...}
+准备创建 DefaultIcbcClient...
+DefaultIcbcClient 创建成功
+积分维护接口原始响应: {...}
+✓ 任务 1 处理成功
+队列处理完成
+```
+
+**异常情况**(现在会记录):
+```
+✗ 任务 1 返回值异常: bool(false)
+接口返回值格式错误
+```
+
+## 🔍 可能的原因分析
+
+### 1. createUniqueNo 函数问题
+
+**检查**:
+```php
+// 在命令行执行
+php -r "require 'application/common.php'; echo createUniqueNo('test', 1);"
+```
+
+**如果不存在**:
+查看 `application/common.php` 文件,确认函数定义
+
+### 2. DefaultIcbcClient 加载问题
+
+**检查**:
+```php
+// 检查文件是否存在
+ls -l extend/icbc/DefaultIcbcClient.php
+```
+
+### 3. 工行接口问题
+
+可能的原因:
+- ❌ 网络不通
+- ❌ 证书问题
+- ❌ 参数错误
+- ❌ URL错误
+
+**测试网络**:
+```bash
+curl -v https://gw.dccnet.com.cn:8084/api/mybank/farm/farmplatf/updateVillagerIntegral/V1
+```
+
+## 📊 数据库状态说明
+
+| Status | 说明 | 处理方式 |
+|--------|------|---------|
+| 0 | 待处理 | 会被定时任务处理 |
+| 1 | 处理中 | 如果超过5分钟,说明卡住了 |
+| 2 | 成功 | 已完成,不再处理 |
+| 3 | 失败 | 重试3次后仍失败 |
+
+## 🎯 完整的排查流程
+
+### 1. 运行测试脚本
+```bash
+php test_icbc_queue.php
+```
+
+### 2. 查看具体的错误信息
+```sql
+SELECT id, mobile_phone, status, retry_count, error_msg, result
+FROM icbc_queue 
+WHERE status IN (1, 3)
+ORDER BY id DESC
+LIMIT 10;
+```
+
+### 3. 查看日志
+```bash
+# 查看今天的日志
+cat runtime/log/$(date +%y%m%d).log | grep -i icbc
+```
+
+### 4. 手动测试单个任务
+```php
+// 创建测试文件 test_single.php
+<?php
+define('APP_PATH', __DIR__ . '/application/');
+require __DIR__ . '/thinkphp/start.php';
+
+$task = \think\Db::name('icbc_queue')->where('id', 1)->find();
+$paper = new \addons\exam\controller\Paper();
+$result = $paper->update_villager_integral(
+    $task['mobile_phone'],
+    $task['integral_value'],
+    $task['integral_type'],
+    [$task['nickname']]
+);
+var_dump($result);
+```
+
+## 🔧 常见问题
+
+### Q1: 还是返回 false 怎么办?
+
+**A**: 查看详细日志:
+```bash
+tail -100 runtime/log/$(date +%y%m%d).log
+```
+
+### Q2: 日志显示 "类不存在" 或 "函数不存在"?
+
+**A**: 检查文件路径和 composer autoload
+
+### Q3: 任务一直卡在 status=1?
+
+**A**: 运行修复脚本:
+```bash
+php test_icbc_queue.php
+# 选择 y 重置卡住的任务
+```
+
+### Q4: 想查看具体是哪一步出错?
+
+**A**: 在 `update_villager_integral` 方法中每一步都有日志,查看日志文件即可定位
+
+## 📌 后续建议
+
+1. **配置定时任务**
+```bash
+# 每分钟执行一次
+* * * * * cd /path/to/shequ && php think icbc_queue >> /tmp/icbc_queue.log 2>&1
+```
+
+2. **定期清理历史数据**
+```sql
+-- 删除30天前的成功记录
+DELETE FROM icbc_queue 
+WHERE status = 2 
+AND updatetime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY));
+```
+
+3. **监控失败率**
+```sql
+-- 查看失败率
+SELECT 
+    COUNT(CASE WHEN status = 2 THEN 1 END) as success_count,
+    COUNT(CASE WHEN status = 3 THEN 1 END) as fail_count,
+    COUNT(*) as total_count,
+    ROUND(COUNT(CASE WHEN status = 3 THEN 1 END) * 100.0 / COUNT(*), 2) as fail_rate
+FROM icbc_queue;
+```
+
+## 💡 总结
+
+修复重点:
+1. ✅ 添加了返回值类型检查(防止 false 导致的问题)
+2. ✅ 添加了详细的日志记录(方便排查)
+3. ✅ 添加了依赖检查(提前发现问题)
+4. ✅ 提供了修复工具(快速恢复)
+
+现在执行 `php test_icbc_queue.php` 可以快速诊断和修复问题!
+