- 增加鉴权:监控调用须带 ?key=monitor_key;密钥首次调用时自动生成 (64hex,持久化到 sq_config setname='monitor_key',setname.inc.php 增加种子行),hash_equals 校验,不匹配返回 403。杜绝匿名触发 补单/清库。key 可通过 SELECT setvalue FROM sq_config WHERE setname='monitor_key' 获取,配置到 cron/监控服务。 - 修复 if(!!$result = select_limit_row(...) && count(...) > 0) 运算符优先级断逻辑:拆为先取值再判断。 - 修复未定义变量 $tradeno(原 update 条件引用了从未赋值的 $tradeno)。 - trade_do 补单改为仅在状态 2->3 翻转成功(影响行数>0)时执行; 影响行=0(已被先行进程完成)则跳过,防并发/重复补单。 - 修复 sq_apps 空表时 !array() 为真 导致误报"获取应用信息发生错误" (改为 false === 判断)。 - 结尾输出 JSON 结果(log/error),便于监控服务核对。 验证: 无key/错key 均 403;正确key 200;构造 status=2 测试订单, 首跑补单(agent 余额 100->110,trade 2->3),二跑无重复补单。 php -l 通过。
95 lines
4.1 KiB
PHP
95 lines
4.1 KiB
PHP
<?php
|
||
/**
|
||
* 未处理数据监控程序:自动补单(已支付未完成订单)+ 清理数据库中冗余数据。
|
||
*
|
||
* 安全加固 (P0-03):
|
||
* 1. 增加鉴权:监控服务须以 {站点}/api/monitor.php?key={monitor_key} 调用;
|
||
* monitor_key 首次调用时自动生成并持久化到 sq_config(setname='monitor_key'),
|
||
* 杜绝匿名触发补单/清库。
|
||
* 2. 修复原 `if(!!$result = ... && count(...) > 0)` 断逻辑(运算符优先级错误)。
|
||
* 3. 修复未定义变量 $tradeno;trade_do 仅在状态翻转成功(影响行数>0)时执行,
|
||
* 已被先行进程完成(影响行=0)则跳过,防止重复补单。
|
||
*
|
||
* 用法:配置您的监控服务/cron 定时访问:
|
||
* http(s)://您的域名/api/monitor.php?key=<monitor_key>
|
||
* key 值可通过数据库查询:SELECT setvalue FROM sq_config WHERE setname='monitor_key';
|
||
*/
|
||
|
||
//设置链接断开仍然处理
|
||
set_time_limit(0);
|
||
ignore_user_abort(1);
|
||
|
||
//调用核心文件
|
||
include __DIR__ . '/../function/function_core.php';
|
||
|
||
/* --- 鉴权 (P0-03):key 缺失或不匹配直接拒绝 --- */
|
||
$monitor_key = isset($G['config']['monitor_key']) ? (string)$G['config']['monitor_key'] : '';
|
||
if (strlen($monitor_key) < 32) {
|
||
//尚未生成(如刚安装):生成一次并持久化(function_core 已保证 sq_config 中存在该键行)
|
||
$monitor_key = bin2hex(random_bytes(32));
|
||
updateset('monitor_key', $monitor_key);
|
||
}
|
||
if (!isset($_GET['key']) || !hash_equals($monitor_key, (string)$_GET['key'])) {
|
||
http_response_code(403);
|
||
die('403 Forbidden');
|
||
}
|
||
|
||
//获取已支付但未同步的订单信息
|
||
$error = array();
|
||
$log = array();
|
||
$result = $db->select_limit_row('sq_trade', '*', '', 5, array('status' => '2'), 'AND');
|
||
if (is_array($result) && count($result) > 0) {
|
||
include __DIR__ . '/../function/trade.inc.php';
|
||
foreach ($result as $tradeinfo) {
|
||
$tradeno = $tradeinfo['tradeno'];
|
||
/*
|
||
* 幂等补单 (P0-03):先把状态 2 -> 3(WHERE 带 status=2,翻转天然幂等),
|
||
* 仅当影响行数 > 0(本进程确实完成了翻转)才执行补单逻辑。
|
||
*/
|
||
if ($db->update('sq_trade', array('tradeno' => $tradeno, 'status' => '2'), 'AND', array('status' => 3))) {
|
||
if ($db->affected_num() > 0) {
|
||
$log[] = '[监控信息] 订单 ' . $tradeno . ' 状态翻转成功,开始补单';
|
||
trade_do($tradeinfo);
|
||
} else {
|
||
$log[] = '[监控信息] 订单 ' . $tradeno . ' 已由先行进程完成,跳过';
|
||
}
|
||
} else {
|
||
$error[] = '[监控信息]订单状态更新失败:' . $tradeno . '[Error:' . $db->geterror() . ']';
|
||
}
|
||
}
|
||
}
|
||
|
||
//清理初始化后60分钟未操作的Token数据
|
||
if ($db->delete('sq_token', '`lastest` = 0 AND `addtime` < ' . (time() - 3600) . ' AND `start` = 0', '')) {
|
||
$num = $db->affected_num();
|
||
if ($num > 0) {
|
||
$log[] = '[监控信息] 清理初始化后60分钟未操作的Token数据' . $num . '条';
|
||
}
|
||
} else {
|
||
$log[] = '[监控信息] 清理过程中发生错误:' . $db->geterror();
|
||
}
|
||
|
||
//清理心跳过期数据
|
||
$result = $db->select_all_row('sq_apps', 'ID,appname,onlinesecond');
|
||
if (false === $result) {
|
||
$log[] = '[监控信息] 获取应用信息发生错误:' . $db->geterror();
|
||
} else {
|
||
foreach ($result as $appinfo) {
|
||
if ($appinfo['onlinesecond'] > 0) {
|
||
$OutTime = intval($appinfo['onlinesecond'] * 3);
|
||
if ($db->delete('sq_token', '`appid` = ' . intval($appinfo['ID']) . ' AND `lastest` < ' . $OutTime, '')) {
|
||
$num = $db->affected_num();
|
||
if ($num > 0) {
|
||
$log[] = '[监控信息] 清理 ' . $appinfo['appname'] . ' 过期Token数据' . $num . '条';
|
||
}
|
||
} else {
|
||
$log[] = '[监控信息] 清理 ' . $appinfo['appname'] . ' 过期数据发生错误:' . $db->geterror();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//输出执行结果,便于监控服务/调用方查看
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
echo json_encode(array('code' => 1, 'log' => $log, 'error' => $error), JSON_UNESCAPED_UNICODE);
|