From 4daa0b61856a4f5240b4a0e110fc9592a8eb49c4 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Fri, 10 Jul 2026 20:51:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8D=95=E7=A8=8B=E7=A5=A8=E8=B4=AD=E7=A5=A8=E5=B9=B6=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E7=89=88=E6=9C=AC=E8=87=B31.0.18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 升级package.json版本至1.0.18;重构update_gate.lua和update_machine.lua,支持--silent静默运行参数,统一日志输出逻辑;优化ticketmachine.lua和gate.lua的自动更新触发逻辑,新增设备空闲检测,避免打断用户操作,仅在空闲时执行静默更新后重启设备;修复ticketmachine.lua中state变量重复声明的问题;移除Web端ticket-order相关的单程票购票UI与业务逻辑,简化购票流程;将gate.lua中的版本检查间隔从5秒调整为60秒,优化设备ID显示格式。 --- gate.lua | 52 +++++++++++----- package.json | 2 +- ticketmachine.lua | 31 +++++++--- update_gate.lua | 37 +++++++---- update_machine.lua | 37 +++++++---- web/ticket-order.html | 38 ------------ web/ticket-order.js | 140 +++--------------------------------------- 7 files changed, 114 insertions(+), 223 deletions(-) diff --git a/gate.lua b/gate.lua index e7a6158..4b64024 100644 --- a/gate.lua +++ b/gate.lua @@ -2,7 +2,7 @@ local DEFAULT_SERVER_BASE = "http://ticket.fse-media.group" local DEFAULT_SERVER_PATH = "/api/tickets/check" local GATE_OPEN_SECONDS = 2 local VERSION = "v1.5.10" -local VERSION_CHECK_INTERVAL = 5 +local VERSION_CHECK_INTERVAL = 60 local CONFIG_PATH = "gate_config.json" @@ -92,6 +92,30 @@ local serverConnected = nil local serverLastChangeTs = 0 local expectedGateVersion = nil local versionMismatch = nil +local pendingGateUpdate = false +local gateBusyUntilTs = 0 + +local function nowMs() + return (os.epoch and os.epoch("utc")) or (os.time() * 1000) +end + +local function markGateBusy(seconds) + local secondsNum = tonumber(seconds) or 0 + local untilTs = nowMs() + math.max(1000, math.floor(secondsNum * 1000)) + if untilTs > gateBusyUntilTs then + gateBusyUntilTs = untilTs + end +end + +local function isGateIdleForUpdate() + return nowMs() >= gateBusyUntilTs +end + +local function runSilentGateUpdate() + local baseEnv = (getfenv and getfenv()) or _ENV or _G + local env = setmetatable({ AUTO_UPDATE_SILENT = true }, { __index = baseEnv }) + return os.run(env, "update_gate.lua", "--silent") +end local function setServerConnected(ok) if serverConnected == ok then return end @@ -997,7 +1021,7 @@ local function currentDeviceId() if label and label ~= "" then return label end - return "闸机#" .. tostring(os.getComputerID()) + return "#" .. tostring(os.getComputerID()) end local function syncICCardState(cardId, payload) @@ -1353,6 +1377,7 @@ local versionTimer = os.startTimer(VERSION_CHECK_INTERVAL) while true do local ev = pack(os.pullEvent()) if ev[1] == "ticket_scanned" or ev[1] == "ic_card_scanned" then + markGateBusy(GATE_OPEN_SECONDS + 2) processInspectionEvent(ev[1], ev) os.sleep(0.35) drawReadyScreen() @@ -1361,19 +1386,16 @@ while true do refreshRemoteLuaVersion(guessBaseFromStatusURL(serverURL)) end) if versionMismatch == true then - pcall(function() - clear() - local w, h = termDev.getSize() - centerText(math.floor(h / 2), "Auto Updating...", colors.yellow) - end) - pcall(function() - if shell and type(shell.run) == "function" then - shell.run("update_gate.lua") - else - os.run(getfenv and getfenv() or _ENV, "update_gate.lua") - end - end) - os.reboot() + pendingGateUpdate = true + elseif versionMismatch == false then + pendingGateUpdate = false + end + if pendingGateUpdate and isGateIdleForUpdate() then + local ok, updated = pcall(runSilentGateUpdate) + if ok and updated then + os.reboot() + return + end end drawReadyScreen() versionTimer = os.startTimer(VERSION_CHECK_INTERVAL) diff --git a/package.json b/package.json index 44e8e9b..1ec70bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ticketmachine", - "version": "1.0.17", + "version": "1.0.18", "main": "server/index.js", "scripts": { "start": "node server/index.js", diff --git a/ticketmachine.lua b/ticketmachine.lua index b352cbd..977486c 100644 --- a/ticketmachine.lua +++ b/ticketmachine.lua @@ -1,6 +1,7 @@ local CURRENT_STATION_CODE = 'Ticket-Machine' local API_BASE = 'http://ticket.fse-media.group/api' local VERSION = 'v1.5.12' +local UPDATE_CHECK_INTERVAL = 60 -- ########################### -- Core HTTP & JSON Utilities @@ -13,6 +14,8 @@ end local serverConnected = nil local versionMismatch = nil +local pendingMachineUpdate = false +local state local function normalizeVersionTag(v) local s = tostring(v or ''):gsub('^%s+', ''):gsub('%s+$', '') @@ -652,19 +655,27 @@ local function clickSound() end -- ########################### -- Background Tasks -- ########################### +local function runSilentMachineUpdate() + local baseEnv = (getfenv and getfenv()) or _ENV or _G + local env = setmetatable({ AUTO_UPDATE_SILENT = true }, { __index = baseEnv }) + return os.run(env, 'update_machine.lua', '--silent') +end + local function backgroundSyncTask() while true do - sleep(5) + sleep(UPDATE_CHECK_INTERVAL) pcall(refreshConfigOnce) if versionMismatch == true then - pcall(function() - if shell and type(shell.run) == "function" then - shell.run("update_machine.lua") - else - os.run(getfenv and getfenv() or _ENV, "update_machine.lua") - end - end) - -- Removed os.reboot() to prevent interrupting user unexpectedly + pendingMachineUpdate = true + elseif versionMismatch == false then + pendingMachineUpdate = false + end + if pendingMachineUpdate and state and tostring(state.page or 'home') == 'home' then + local ok, updated = pcall(runSilentMachineUpdate) + if ok and updated then + os.reboot() + return + end end end end @@ -966,7 +977,7 @@ end -- ########################### -- Pages & Navigation -- ########################### -local state = { +state = { page = 'home', stationName = (CFG.current_station and (CFG.current_station.en_name or CFG.current_station.name)) or 'Station', stationCode = (CFG.current_station and CFG.current_station.code) or CURRENT_STATION_CODE, diff --git a/update_gate.lua b/update_gate.lua index ae262a8..eadf06b 100644 --- a/update_gate.lua +++ b/update_gate.lua @@ -1,5 +1,12 @@ - local URL_GATE = "http://gitea.fse-media.group/Henry_Du/FSE-Ticket.sys/raw/branch/main/gate.lua" +local args = { ... } +local silent = AUTO_UPDATE_SILENT == true or args[1] == "--silent" + +local function log(msg) + if not silent then + print(msg) + end +end local function writeFile(path, content, binary) local mode = binary and "wb" or "w" @@ -54,25 +61,29 @@ local function httpGet(url) end end -term.clear() -term.setCursorPos(1, 1) -print("Ticket Gate Updater") -print("") -print("Downloading gate program...") +if not silent then + term.clear() + term.setCursorPos(1, 1) + log("Ticket Gate Updater") + log("") +end +log("Downloading gate program...") local ok, code = httpGet(URL_GATE) if not ok or type(code) ~= "string" or #code == 0 then - print("Download failed: " .. tostring(code or "")) - return + error("Download failed: " .. tostring(code or "")) end if not atomicWrite("startup.lua", code, false) then - print("Write failed: startup.lua") - return + error("Write failed: startup.lua") end atomicWrite("startup", code, false) if fs.exists("gate.lua") then atomicWrite("gate.lua", code, false) end -print("") -print("Done.") -print("Reboot the computer to apply the update.") +if not silent then + log("") + log("Done.") + log("Reboot the computer to apply the update.") +end + +return true diff --git a/update_machine.lua b/update_machine.lua index 2ab3a15..73c476f 100644 --- a/update_machine.lua +++ b/update_machine.lua @@ -1,5 +1,12 @@ - local URL_MACHINE = "http://gitea.fse-media.group/Henry_Du/FSE-Ticket.sys/raw/branch/main/ticketmachine.lua" +local args = { ... } +local silent = AUTO_UPDATE_SILENT == true or args[1] == "--silent" + +local function log(msg) + if not silent then + print(msg) + end +end local function writeFile(path, content, binary) local mode = binary and "wb" or "w" @@ -54,25 +61,29 @@ local function httpGet(url) end end -term.clear() -term.setCursorPos(1, 1) -print("Ticket Machine Updater") -print("") -print("Downloading ticket machine program...") +if not silent then + term.clear() + term.setCursorPos(1, 1) + log("Ticket Machine Updater") + log("") +end +log("Downloading ticket machine program...") local ok, code = httpGet(URL_MACHINE) if not ok or type(code) ~= "string" or #code == 0 then - print("Download failed: " .. tostring(code or "")) - return + error("Download failed: " .. tostring(code or "")) end if not atomicWrite("startup.lua", code, false) then - print("Write failed: startup.lua") - return + error("Write failed: startup.lua") end atomicWrite("startup", code, false) if fs.exists("ticketmachine.lua") then atomicWrite("ticketmachine.lua", code, false) end -print("") -print("Done.") -print("Reboot the computer to apply the update.") +if not silent then + log("") + log("Done.") + log("Reboot the computer to apply the update.") +end + +return true diff --git a/web/ticket-order.html b/web/ticket-order.html index 0b0ba99..6e435da 100644 --- a/web/ticket-order.html +++ b/web/ticket-order.html @@ -47,29 +47,6 @@
-
-
- 票种 - 请选择购买的车票类型 -
-
- - -
-
-
@@ -98,21 +75,6 @@
- -
diff --git a/web/ticket-order.js b/web/ticket-order.js index b36867c..b0ab93e 100644 --- a/web/ticket-order.js +++ b/web/ticket-order.js @@ -565,43 +565,9 @@ async function previewPrice(force = false){ const seq = ++previewSeq; - const ticketClass = document.querySelector('input[name="ticketClass"]:checked').value; const type = document.querySelector('input[name="trainType"]:checked').value; const trips = normalizeTrips(); - if (ticketClass === 'single') { - const lineName = document.getElementById('singleSegmentSelection').value; - const priceStr = document.getElementById('singleSegmentPrice').value; - if (!lineName || !priceStr) { - priceBox.innerHTML = '

请选择单程区间

'; - return; - } - const cost = Number(priceStr) * trips; - priceBox.innerHTML = ` -
- 线路 - ${lineName} -
-
- 区间 - 任意单区间 -
-
- 车型 - ${type} -
-
- 单价 - $${priceStr} -
-
- 总价 - $${cost} -
- `; - return; - } - const from = (fromEl.value||'').trim(); const to = (toEl.value||'').trim(); if(!from || !to){ priceBox.innerHTML = '

请选择起点与终点

'; return; } @@ -679,29 +645,18 @@ // Create Order with auto-preview async function createOrder(){ - const ticketClass = document.querySelector('input[name="ticketClass"]:checked').value; const type = document.querySelector('input[name="trainType"]:checked').value; const trips = normalizeTrips(); const ride_date = new Date().toISOString().slice(0, 10); let payload = null; - if (ticketClass === 'single') { - const lineName = document.getElementById('singleSegmentSelection').value; - const priceStr = document.getElementById('singleSegmentPrice').value; - if (!lineName || !priceStr) { - alert('请选择单程区间'); - return; - } - payload = { start: lineName, terminal: 'SINGLE_SEGMENT', train_type: 'single', price: Number(priceStr), trips, ride_date }; - } else { - const from = (fromEl.value||'').trim(); - const to = (toEl.value||'').trim(); - if(!from || !to){ alert('请完整填写信息'); return; } - - // Auto-fetch price before creating - await previewPrice(true); - payload = { start: from, terminal: to, train_type: type, trips, ride_date }; - } + const from = (fromEl.value||'').trim(); + const to = (toEl.value||'').trim(); + if(!from || !to){ alert('请完整填写信息'); return; } + + // Auto-fetch price before creating + await previewPrice(true); + payload = { start: from, terminal: to, train_type: type, trips, ride_date }; try{ const res = await fetch('/api/public/orders', { method:'POST', headers:{ 'Content-Type':'application/json' }, body: JSON.stringify(payload) }); @@ -740,89 +695,8 @@ document.querySelectorAll('input[name="trainType"]').forEach(el => { el.onchange = () => { if(fromEl.value && toEl.value) previewPrice(); }; }); - document.querySelectorAll('input[name="ticketClass"]').forEach(r => r.addEventListener('change', toggleTicketClass)); if (tripsEl) { tripsEl.oninput = () => { if(fromEl.value && toEl.value) previewPrice(); }; tripsEl.onchange = () => { normalizeTrips(); if(fromEl.value && toEl.value) previewPrice(); }; } - - function toggleTicketClass() { - const isSingle = document.querySelector('input[name="ticketClass"]:checked').value === 'single'; - if (isSingle) { - document.getElementById('normalTicketView').style.display = 'none'; - document.getElementById('singleTicketView').style.display = 'block'; - document.getElementById('tripsBlock').style.display = 'none'; - if (tripsEl) tripsEl.value = 1; - renderSingleSegments(); - } else { - document.getElementById('normalTicketView').style.display = 'block'; - document.getElementById('singleTicketView').style.display = 'none'; - document.getElementById('tripsBlock').style.display = 'block'; - } - previewPrice(true); - } - - async function renderSingleSegments() { - const listEl = document.getElementById('singleSegmentList'); - if (listEl.innerHTML.includes('jr-result-row')) return; // Already rendered - listEl.innerHTML = '
加载区间数据...
'; - try { - const [lines, fares] = await Promise.all([ - fetch('/api/public/lines').then(r=>r.json()), - fetch('/api/public/fares').then(r=>r.json()) - ]); - - const type = document.querySelector('input[name="trainType"]:checked').value; - const segments = []; - - lines.forEach(line => { - const stops = Array.isArray(line.stops) ? line.stops : (Array.isArray(line.站点列表) ? line.站点列表 : []); - const lineFares = new Set(); - for (let i = 0; i < stops.length - 1; i++) { - const a = stops[i], b = stops[i+1]; - const f = fares.find(x => (x.from === a && x.to === b) || (x.from === b && x.to === a)); - if (f) { - const cost = type === 'Express' ? (f.cost_express ?? f.cost ?? 0) : (f.cost_regular ?? f.cost ?? 0); - lineFares.add(Number(cost)); - } - } - lineFares.forEach(cost => { - segments.push({ - lineName: line.name || line.线路名称 || '未知线路', - color: line.color || line.颜色 || '#93a2b7', - cost - }); - }); - }); - - if (segments.length === 0) { - listEl.innerHTML = '

暂无单程区间数据

'; - return; - } - - let html = ''; - segments.forEach((seg, idx) => { - html += ` - - `; - }); - listEl.innerHTML = html; - } catch (e) { - listEl.innerHTML = '
加载区间数据失败
'; - } - } - - window.selectSingleSegment = (lineName, cost) => { - document.getElementById('singleSegmentSelection').value = lineName; - document.getElementById('singleSegmentPrice').value = cost; - previewPrice(true); - }; })();