Files
oil/server-tp6/deploy/qyt_consume_push.php
T
vipnub 45452c1799 1、合作商余额查询
2、分油账户司机可用额度
2026-10-08 23:12:33 +08:00

200 lines
8.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | 模拟企油通 7.10「运单加油明细推送」 —— 联调 / 补推用(CLI)
// +----------------------------------------------------------------------
// | 按企油通的真实口径推送:multipart/form-data,业务参数放在表单字段 params 里。
// |
// | 用法(在站点根目录执行):
// | php deploy/qyt_consume_push.php --order=6 --amount=100
// | php deploy/qyt_consume_push.php --code=HMCTEST20260914233556192 --amount=100
// | php deploy/qyt_consume_push.php --order=6 --amount=30 --dry-run # 只打印报文不发送
// |
// | 常用参数:
// | --order=订单ID 从数据库取运单号等信息(推荐)
// | --code=运单唯一流水 不查库直接指定 waybill_no_code
// | --amount=金额(元) 本次加油占用金额,默认取订单剩余未加油金额
// | --order-code=消费单号 去重键,默认自动生成;想测【重复推送】就填上次用过的
// | --station=加油站名 默认「模拟加油站」
// | --url=回调地址 默认取 .env 的 QYT.CONSUME_NOTIFY_URL
// | --dry-run 只打印,不实际发送
// |
// | ⚠️ 这会写入真实的加油流水、累计订单已加油金额、并触发给下单平台的加油通知,
// | 只用于联调环境。联调结束请删除本文件。
// +----------------------------------------------------------------------
declare (strict_types=1);
if (PHP_SAPI !== 'cli') {
http_response_code(403);
exit('仅限命令行执行');
}
$root = dirname(__DIR__);
// ---------------- 参数解析 ----------------
$opt = [];
foreach (array_slice($argv, 1) as $arg) {
if (preg_match('/^--([a-zA-Z0-9\-_]+)(?:=(.*))?$/', $arg, $m)) {
$opt[str_replace('-', '_', $m[1])] = $m[2] ?? '1';
}
}
$orderId = isset($opt['order']) ? (int) $opt['order'] : 0;
$code = isset($opt['code']) ? trim((string) $opt['code']) : '';
$dryRun = !empty($opt['dry_run']);
$station = isset($opt['station']) ? (string) $opt['station'] : '模拟加油站';
$orderCode = isset($opt['order_code']) ? trim((string) $opt['order_code']) : '';
if ($orderId <= 0 && $code === '') {
exit("用法:php deploy/qyt_consume_push.php --order=<订单ID> [--amount=金额] [--dry-run]\n"
. " php deploy/qyt_consume_push.php --code=<运单唯一流水> --amount=金额\n"
. "详细参数见本文件头部注释。\n");
}
// ---------------- 读取 .env ----------------
$ini = @parse_ini_file($root . '/.env', true, INI_SCANNER_RAW) ?: [];
$db = $ini['DATABASE'] ?? [];
$qyt = $ini['QYT'] ?? [];
$url = isset($opt['url']) ? (string) $opt['url'] : trim((string) ($qyt['CONSUME_NOTIFY_URL'] ?? ''));
if ($url === '') {
$url = 'http://127.0.0.1/qytconsume.php';
}
// ---------------- 取订单(可选) ----------------
$pdo = null;
$order = null;
try {
$pdo = new PDO(
'mysql:host=' . ($db['HOSTNAME'] ?? '127.0.0.1') . ';port=' . ($db['HOSTPORT'] ?? '3306')
. ';dbname=' . ($db['DATABASE'] ?? 'oil') . ';charset=' . ($db['CHARSET'] ?? 'utf8'),
$db['USERNAME'] ?? 'root', $db['PASSWORD'] ?? '',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
} catch (Throwable $e) {
echo "提示:连不上数据库({$e->getMessage()}),将只发送推送、不做前后对比\n\n";
}
if ($pdo) {
$sql = $orderId > 0
? 'SELECT * FROM yoshop_partners_order WHERE order_id = ? LIMIT 1'
: 'SELECT * FROM yoshop_partners_order WHERE waybill_no_code = ? LIMIT 1';
$st = $pdo->prepare($sql);
$st->execute([$orderId > 0 ? $orderId : $code]);
$order = $st->fetch() ?: null;
if (!$order && $orderId > 0) {
exit("未找到订单 order_id={$orderId}\n");
}
}
if ($order) {
$code = (string) $order['waybill_no_code'];
if ($code === '') {
exit("订单 {$order['order_id']} 还没有运单流水号(waybill_no_code 为空),说明 7.7 还没成功下发,不能模拟加油\n");
}
}
if ($code === '') {
exit("缺少 waybill_no_code:请用 --code 指定,或用 --order 指定订单\n");
}
// ---------------- 金额 ----------------
$remain = $order ? round((float) $order['total_price'] - (float) $order['used_amount'], 2) : 0.0;
$amount = isset($opt['amount']) ? round((float) $opt['amount'], 2) : $remain;
if ($amount <= 0) {
exit("加油金额必须大于 0(订单剩余未加油金额为 " . number_format($remain, 2) . " 元,可用 --amount 指定)\n");
}
if ($order && $amount > $remain + 0.005) {
echo "⚠️ 本次金额 {$amount} 元 超过订单剩余未加油金额 " . number_format($remain, 2) . " 元,"
. "服务端会照常入账并写超额告警日志\n";
}
// ---------------- 组装报文(字段与 7.10 文档一致) ----------------
$fen = (int) round($amount * 100);
$item = [
'waybill_no' => $order['waybill_no'] ?? ('SIMW' . $code),
'order_code' => $orderCode !== '' ? $orderCode : ('SIM' . date('YmdHis') . mt_rand(1000, 9999)),
'waybill_no_code' => $code,
'quota_id' => $order['qyt_order_code'] ?? ('SIMQ' . mt_rand(100000, 999999)),
'passageway' => ($order['passageway'] ?? 'alipay') === 'pingan' ? 2 : 1,
'station_code' => '',
'station_name' => $station,
'pay_fee' => $fen,
'pay_time' => date('Y-m-d H:i:s'),
'consume_amount' => $fen,
'litter' => isset($opt['litter']) ? (string) $opt['litter'] : '',
'oil_name' => isset($opt['oil_name']) ? (string) $opt['oil_name'] : '',
'oil_price' => '',
'actual_oil_price' => '',
'scene' => 'oil',
];
$params = json_encode(['order_list' => [$item]], JSON_UNESCAPED_UNICODE);
echo "回调地址:{$url}\n";
if ($order) {
printf("订单:order_id=%s 油卡号=%s 金额=%s 已加油=%s 剩余=%s 加油次数=%s\n",
$order['order_id'], $order['oilCardNo'], $order['total_price'], $order['used_amount'],
number_format($remain, 2), $order['used_num']);
}
echo "本次加油:{$amount} 元({$fen} 分) 消费单号(去重键):{$item['order_code']}\n";
echo "params:{$params}\n\n";
if ($dryRun) {
echo "--dry-run:未发送\n";
exit;
}
// ---------------- 发送:multipart/form-data,params 放表单字段 ----------------
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
// 传数组 => cURL 自动用 multipart/form-data 编码,与企油通口径一致
CURLOPT_POSTFIELDS => ['params' => $params],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
]);
$resp = curl_exec($ch);
$http = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
echo "HTTP {$http}\n响应:" . ($resp === false ? "cURL 失败:{$err}" : $resp) . "\n";
$ok = is_string($resp) && strpos($resp, '"100"') !== false;
echo $ok ? "接收端已受理(code=100)\n" : "⚠️ 接收端未返回 code=100,请查 runtime/storelog 下当天的 qyt_*.txt\n";
// ---------------- 等队列处理并对比结果 ----------------
if (!$ok || !$pdo || !$order) {
echo "\n提示:真正的落库与订单累计由 callback:queue 进程完成,请确认该进程在运行。\n";
exit;
}
echo "\n等待 callback:queue 处理";
$after = null;
for ($i = 0; $i < 10; $i++) {
sleep(1);
echo '.';
$st = $pdo->prepare('SELECT used_amount, used_num, use_status, refund_status FROM yoshop_partners_order WHERE order_id = ?');
$st->execute([$order['order_id']]);
$after = $st->fetch();
if ($after && (float) $after['used_amount'] > (float) $order['used_amount']) {
break;
}
}
echo "\n";
if ($after && (float) $after['used_amount'] > (float) $order['used_amount']) {
printf("✅ 已入账:已加油 %s -> %s,加油次数 %s -> %s,加油状态=%s\n",
$order['used_amount'], $after['used_amount'], $order['used_num'], $after['used_num'],
(int) $after['use_status'] === 1 ? '已加油' : '未加油');
$st = $pdo->prepare('SELECT id, order_code, consume_amount, notify_status FROM yoshop_qyt_consume WHERE order_id = ? ORDER BY id DESC LIMIT 3');
$st->execute([$order['order_id']]);
echo "最近消费流水:\n";
foreach ($st->fetchAll() as $r) {
printf(" #%s %s %s元 通知状态=%s\n", $r['id'], $r['order_code'],
number_format($r['consume_amount'] / 100, 2), $r['notify_status'] ? '已通知' : '待通知');
}
} else {
echo "⚠️ 10 秒内订单没有变化,可能原因:\n"
. " 1) callback:queue 进程没在跑(supervisorctl status 看一下)\n"
. " 2) 该消费单号已存在,被唯一键判为重复推送(换一个 --order-code 再试)\n"
. " 3) 处理异常,查 runtime/storelog 下当天的 " . date('YmdH') . ".txt\n";
}