修复: 订单记录页加载卡死(增加请求超时与重试) + 订单接口释放会话锁并对database is locked重试
This commit is contained in:
+31
-12
@@ -151,29 +151,48 @@ function cashierPlayerCheck() {
|
||||
// ===== 订单列表 =====
|
||||
function cashierOrderList() {
|
||||
$role = requireCashierOrAdminSession();
|
||||
// 收银员只能看自己的订单
|
||||
$c = null;
|
||||
if ($role === 'cashier') {
|
||||
$c = getCurrentCashier();
|
||||
}
|
||||
// ★ 尽早释放会话锁:查询期间不再占用 session 文件,避免阻塞同会话并发请求
|
||||
if (session_status() === PHP_SESSION_ACTIVE) session_write_close();
|
||||
|
||||
$db = getDB();
|
||||
$limit = (int)getParam('limit', 100);
|
||||
if ($limit <= 0 || $limit > 500) $limit = 100;
|
||||
$sql = "SELECT * FROM cashier_orders";
|
||||
$params = [];
|
||||
// 收银员只能看自己的订单
|
||||
if ($role === 'cashier') {
|
||||
$c = getCurrentCashier();
|
||||
if ($role === 'cashier' && $c) {
|
||||
$sql .= " WHERE operator_name = :op";
|
||||
$params[':op'] = $c['username'];
|
||||
}
|
||||
$sql .= " ORDER BY created_at DESC LIMIT " . (int)$limit;
|
||||
$stmt = $db->prepare($sql);
|
||||
foreach ($params as $k => $v) {
|
||||
$stmt->bindValue($k, $v, SQLITE3_TEXT);
|
||||
}
|
||||
$result = $stmt->execute();
|
||||
|
||||
// ★ 重试规避 database is locked(Java 高频写入时 SQLite 偶发锁,最多重试4次)
|
||||
$rows = [];
|
||||
while ($r = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||
if (!empty($r['items_detail'])) {
|
||||
$r['items_detail'] = json_decode($r['items_detail'], true) ?: [];
|
||||
$attempts = 0;
|
||||
while ($attempts < 4) {
|
||||
$attempts++;
|
||||
$stmt = $db->prepare($sql);
|
||||
if (!$stmt) break;
|
||||
foreach ($params as $k => $v) {
|
||||
$stmt->bindValue($k, $v, SQLITE3_TEXT);
|
||||
}
|
||||
$rows[] = $r;
|
||||
$result = @$stmt->execute();
|
||||
if ($result === false) {
|
||||
$err = $db->lastErrorMsg();
|
||||
if (stripos($err, 'locked') !== false && $attempts < 4) { usleep(200000); continue; }
|
||||
exit(json_encode(['success' => false, 'message' => '查询失败: ' . $err], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
while ($r = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||
if (!empty($r['items_detail'])) {
|
||||
$r['items_detail'] = json_decode($r['items_detail'], true) ?: [];
|
||||
}
|
||||
$rows[] = $r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
exit(json_encode(['success' => true, 'data' => $rows], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
+24
-6
@@ -127,12 +127,24 @@ $initialState = json_encode([
|
||||
<script>
|
||||
const STATE = <?php echo $initialState; ?>;
|
||||
function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g, c=>({'&':'&','<':'<','>':'>','"':'"}[c])); }
|
||||
async function api(url, opts) {
|
||||
const r = await fetch(url, Object.assign({headers:{'X-Requested-With':'orders'}}, opts));
|
||||
return await r.json();
|
||||
async function api(url, opts, timeoutMs = 20000) {
|
||||
const ctrl = new AbortController();
|
||||
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
||||
try {
|
||||
const r = await fetch(url, Object.assign(
|
||||
{headers: {'X-Requested-With': 'orders'}, credentials: 'same-origin'},
|
||||
opts || {},
|
||||
{signal: ctrl.signal}
|
||||
));
|
||||
return await r.json();
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadOrders() {
|
||||
const el = document.getElementById('orderList');
|
||||
el.innerHTML = '<div class="empty-state">加载中…</div>';
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.set('action', 'order_list');
|
||||
@@ -145,10 +157,13 @@ async function loadOrders() {
|
||||
if (pm) params.set('pay_mode', pm);
|
||||
|
||||
const d = await api('api/cashier.php?' + params.toString());
|
||||
const el = document.getElementById('orderList');
|
||||
document.getElementById('orderCount').textContent = '';
|
||||
|
||||
if (!d.success || !d.data || !d.data.length) {
|
||||
if (!d.success) {
|
||||
el.innerHTML = '<div class="empty-state">加载失败:' + esc(d.message || '未知错误') + ',<a href="javascript:loadOrders()" style="color:var(--accent)">点击重试</a></div>';
|
||||
return;
|
||||
}
|
||||
if (!d.data || !d.data.length) {
|
||||
el.innerHTML = '<div class="empty-state">暂无符合条件的订单</div>';
|
||||
return;
|
||||
}
|
||||
@@ -167,7 +182,10 @@ async function loadOrders() {
|
||||
<div class="od">实收 <b style="color:var(--green)">${o.total_price}</b> 债券 · ${new Date((o.created_at||0)*1000).toLocaleString('zh-CN')}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
} catch(e) { document.getElementById('orderList').innerHTML = '<div class="empty-state">加载失败</div>'; }
|
||||
} catch(e) {
|
||||
const msg = (e && e.name === 'AbortError') ? '加载超时(服务器繁忙),' : '加载失败,';
|
||||
el.innerHTML = '<div class="empty-state">' + msg + '<a href="javascript:loadOrders()" style="color:var(--accent)">点击重试</a></div>';
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtmlAttr(str) {
|
||||
|
||||
Reference in New Issue
Block a user