订单前缀配置化+新订单号格式,补单脚本同读前缀;新增online_heartbeat心跳与sync_webtx_receipts回执补推端点

This commit is contained in:
ypsdf1
2026-10-06 18:18:22 +08:00
parent bd47d25165
commit 6a6cb9ec3c
6 changed files with 398 additions and 14 deletions
+103
View File
@@ -0,0 +1,103 @@
<?php
/**
* 收银台订单前缀 / 订单号生成 —— 统一读配置的公共小工具(2026-10-06)
*
* 背景:平台 MySQL 的 pay_order 是多商户共用表,创单与三个补单脚本此前都把 'RE'
* 前缀写死在各自代码里。生产服 / 测试服共用一个前缀就会互相捡单 —— 测试服
* 补单器把生产服的已支付单补进测试库,反过来也一样(就是这次串数据的根因)。
*
* 现在前缀收口到 config.php 的 PAY_ORDER_PREFIX:
* pay.php 创单时用它拼订单号
* poller_online.php 补单时用它 LIKE 扫平台 + strncmp 兜底校验
* pay_poller.php 同上
* recharge_orders_api.php 同上
* ★ 创单端与补单端必须读同一份配置:创单用什么前缀,补单器就用什么前缀扫平台,
* 两边一旦不一致就会「创出来的单补不回来」。这四个文件都要跟着改,只改一个会出事。
*
* ★ 本文件不依赖 core.php,任何脚本(含自包含的 poller)都能直接 require。
* config.php 由 security.php 加载;若仍缺常量(线上老版 config.php),一律回退
* 'RE' 默认值,行为与改造前完全一致,保证向后兼容。
*
* ★ 债券换算(estimateBonds)不在本文件:金额→债券由后台「充值商店配置」
* (shop_configs.bond_reward) 决定,代码不干预比例。
*/
if (!function_exists('payOrderPrefix')) {
/**
* 订单号前缀(永不返回空串;未定义/为空一律回退 'RE')
* 只保留字母数字:它要拼进 SQL 的 LIKE '前缀%',也与平台侧约定一致。
*/
function payOrderPrefix() {
$p = defined('PAY_ORDER_PREFIX') ? trim((string)PAY_ORDER_PREFIX) : '';
if ($p === '') return 'RE';
$p = preg_replace('/[^A-Za-z0-9]/', '', $p);
return $p === '' ? 'RE' : $p;
}
}
if (!function_exists('payOrderPrefixLen')) {
/** 前缀长度(strncmp 用,避免写死 2) */
function payOrderPrefixLen() {
return strlen(payOrderPrefix());
}
}
if (!function_exists('payOrderIsOurs')) {
/**
* 判断一个 out_trade_no 是否本端订单(补单器的兜底校验)
* @param string $outNo
* @return bool
*/
function payOrderIsOurs($outNo) {
$p = payOrderPrefix();
return strncmp((string)$outNo, $p, strlen($p)) === 0;
}
}
if (!function_exists('payOrderPrefixLike')) {
/** SQL 片段:out_trade_no LIKE '前缀%'(前缀只含字母数字,无需转义) */
function payOrderPrefixLike() {
return payOrderPrefix() . '%';
}
}
if (!function_exists('payMakeOrderNo')) {
/**
* 生成本端订单号:前缀 + 年月日时分秒 + 4位随机校验码 + 玩家名
* 例:RE20261006171418Q101youpaishidifu
*
* 加玩家名是为了在共用的 pay_order 表里一眼看出这单属于谁,排查串服问题最快。
* 玩家名只保留 [A-Za-z0-9_](Minecraft 用户名规则),其余字符剔除;
* 若剔除后为空则整段省略,保证订单号前缀/时间戳/校验码绝不被破坏 ——
* 前缀一旦被截断,补单器就再也认不出这单了。
*
* @param string $playerName 下单玩家名
* @return string
*/
function payMakeOrderNo($playerName = '') {
$p = payOrderPrefix();
$stamp = date('YmdHis'); // 14位:20261006171418
$code = strtoupper(substr(str_shuffle('ABCDEFGHJKLMNPQRSTUVWXYZ23456789'), 0, 4)); // 4位校验码(去掉易混的 I/O/0/1)
// 玩家名净化:只留字母数字下划线
$name = preg_replace('/[^A-Za-z0-9_]/', '', (string)$playerName);
if ($name === '') $name = '';
$no = $p . $stamp . $code . $name;
// 长度护栏:平台 out_trade_no 默认 varchar(64)。超长时优先裁短玩家名段,
// 绝不裁前缀/时间戳/校验码。
$max = defined('PAY_ORDER_MAX_LEN') ? (int)PAY_ORDER_MAX_LEN : 64;
if ($max > 0 && strlen($no) > $max) {
$fixed = strlen($p) + strlen($stamp) + strlen($code); // 不可裁部分
$allow = $max - $fixed;
if ($allow <= 0) {
// 极端情况(前缀配得极长):宁可退回不含玩家名的最短形态
$no = $p . $stamp . $code;
} else {
$no = $p . $stamp . $code . substr($name, 0, $allow);
}
}
return $no;
}
}