fix: 修复票据详情设备ID缺失并优化自动更新逻辑
- 为票据详情接口添加设备字段兼容映射,支持从e.device/e.device_id/e.detail?.device获取值 - 更新设备ID缺失问题的调试文档,标记已验证的假设并补充最终结论 - 优化售票机和闸机的自动更新逻辑:缩短版本检查间隔至5秒,添加更新中状态标识,改为事件触发更新
This commit is contained in:
@@ -13,16 +13,17 @@
|
|||||||
## Hypotheses & Verification
|
## Hypotheses & Verification
|
||||||
| ID | Hypothesis | Likelihood | Effort | Evidence |
|
| ID | Hypothesis | Likelihood | Effort | Evidence |
|
||||||
|----|------------|------------|--------|----------|
|
|----|------------|------------|--------|----------|
|
||||||
| A | 后端接口返回的事件对象里根本没有 `device` / `detail.device` 字段 | High | Low | Pending |
|
| A | 后端接口返回的事件对象里根本没有 `device` / `detail.device` 字段 | High | Low | Rejected |
|
||||||
| B | 前端格式化函数读到了值,但后续字符串拼接或编码问题把值丢掉了 | High | Low | Pending |
|
| B | 前端格式化函数读到了值,但后续字符串拼接或编码问题把值丢掉了 | High | Low | Rejected |
|
||||||
| C | 售票与检票两类事件的数据结构不同,当前前端只兼容了其中一类 | Med | Low | Pending |
|
| C | 售票与检票两类事件的数据结构不同,当前前端只兼容了其中一类 | Med | Low | Confirmed |
|
||||||
| D | 设备端 Lua 确实上报了 ID,但服务端在写库或对外返回时被覆盖成空串 | Med | Med | Pending |
|
| D | 设备端 Lua 确实上报了 ID,但服务端在写库或对外返回时被覆盖成空串 | Med | Med | Rejected |
|
||||||
| E | 页面实际运行的不是当前工作区这份前端代码,导致本地修改未生效 | Med | Med | Pending |
|
| E | 页面实际运行的不是当前工作区这份前端代码,导致本地修改未生效 | Med | Med | Pending |
|
||||||
|
|
||||||
## Log Evidence
|
## Log Evidence
|
||||||
- 2026-07-10: 已启动 Debug Server `http://127.0.0.1:7777/event`
|
- 2026-07-10: 已启动 Debug Server `http://127.0.0.1:7777/event`
|
||||||
- 2026-07-10: 已在 `[public.js]` 的票据时间线组装处添加 A/C 埋点
|
- 2026-07-10: 已在 `[public.js]` 的票据时间线组装处添加 A/C 埋点
|
||||||
- 2026-07-10: 已在 `[index.js]` 与 `[ticket-board.html]` 的设备 ID 格式化处添加 B 埋点
|
- 2026-07-10: 已在 `[index.js]` 与 `[ticket-board.html]` 的设备 ID 格式化处添加 B 埋点
|
||||||
|
- 2026-07-10: 本地运行时复现结果:`saleRawDevice="#0"` 但 `saleMappedDevice=null`;同时 `checkRawDevice="#1"` 且 `checkMappedDevice="#1"`,证明 `sale` 映射阶段丢失了 `device`
|
||||||
|
|
||||||
## Verification Conclusion
|
## Verification Conclusion
|
||||||
[Pending]
|
- 已实施最小修复:在公开票据详情的 `sale` 事件映射中保留 `device`
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ local DEFAULT_SERVER_BASE = "http://ticket.fse-media.group"
|
|||||||
local DEFAULT_SERVER_PATH = "/api/tickets/check"
|
local DEFAULT_SERVER_PATH = "/api/tickets/check"
|
||||||
local GATE_OPEN_SECONDS = 2
|
local GATE_OPEN_SECONDS = 2
|
||||||
local VERSION = "v1.5.10"
|
local VERSION = "v1.5.10"
|
||||||
local VERSION_CHECK_INTERVAL = 60
|
local VERSION_CHECK_INTERVAL = 5
|
||||||
|
|
||||||
local CONFIG_PATH = "gate_config.json"
|
local CONFIG_PATH = "gate_config.json"
|
||||||
|
|
||||||
@@ -93,6 +93,7 @@ local serverLastChangeTs = 0
|
|||||||
local expectedGateVersion = nil
|
local expectedGateVersion = nil
|
||||||
local versionMismatch = nil
|
local versionMismatch = nil
|
||||||
local pendingGateUpdate = false
|
local pendingGateUpdate = false
|
||||||
|
local isUpdating = false
|
||||||
local gateBusyUntilTs = 0
|
local gateBusyUntilTs = 0
|
||||||
|
|
||||||
local function nowMs()
|
local function nowMs()
|
||||||
@@ -163,7 +164,11 @@ local function drawVersionIndicator(w)
|
|||||||
if #s == 0 then return end
|
if #s == 0 then return end
|
||||||
if w < #s then return end
|
if w < #s then return end
|
||||||
local markerColor = colors.yellow
|
local markerColor = colors.yellow
|
||||||
if versionMismatch == true then
|
local markerText = "*"
|
||||||
|
if isUpdating then
|
||||||
|
markerColor = colors.yellow
|
||||||
|
markerText = "^ing"
|
||||||
|
elseif versionMismatch == true then
|
||||||
markerColor = colors.red
|
markerColor = colors.red
|
||||||
elseif versionMismatch == false then
|
elseif versionMismatch == false then
|
||||||
markerColor = colors.lime
|
markerColor = colors.lime
|
||||||
@@ -172,9 +177,9 @@ local function drawVersionIndicator(w)
|
|||||||
termDev.setTextColor(colors.gray)
|
termDev.setTextColor(colors.gray)
|
||||||
termDev.setCursorPos(1, 1)
|
termDev.setCursorPos(1, 1)
|
||||||
termDev.write(s)
|
termDev.write(s)
|
||||||
if w >= (#s + 1) then
|
if w >= (#s + #markerText) then
|
||||||
termDev.setTextColor(markerColor)
|
termDev.setTextColor(markerColor)
|
||||||
termDev.write("*")
|
termDev.write(markerText)
|
||||||
end
|
end
|
||||||
termDev.setTextColor(colors.white)
|
termDev.setTextColor(colors.white)
|
||||||
end
|
end
|
||||||
@@ -1374,6 +1379,20 @@ end
|
|||||||
|
|
||||||
local versionTimer = os.startTimer(VERSION_CHECK_INTERVAL)
|
local versionTimer = os.startTimer(VERSION_CHECK_INTERVAL)
|
||||||
|
|
||||||
|
-- 启动时不再自动触发更新检查逻辑,等待事件触发
|
||||||
|
-- if pendingGateUpdate and isGateIdleForUpdate() then
|
||||||
|
-- isUpdating = true
|
||||||
|
-- drawReadyScreen()
|
||||||
|
-- os.sleep(0.5)
|
||||||
|
-- local ok, updated = pcall(runSilentGateUpdate)
|
||||||
|
-- if ok and updated then
|
||||||
|
-- os.reboot()
|
||||||
|
-- return
|
||||||
|
-- end
|
||||||
|
-- isUpdating = false
|
||||||
|
-- drawReadyScreen()
|
||||||
|
-- end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local ev = pack(os.pullEvent())
|
local ev = pack(os.pullEvent())
|
||||||
if ev[1] == "ticket_scanned" or ev[1] == "ic_card_scanned" then
|
if ev[1] == "ticket_scanned" or ev[1] == "ic_card_scanned" then
|
||||||
@@ -1381,21 +1400,29 @@ while true do
|
|||||||
processInspectionEvent(ev[1], ev)
|
processInspectionEvent(ev[1], ev)
|
||||||
os.sleep(0.35)
|
os.sleep(0.35)
|
||||||
drawReadyScreen()
|
drawReadyScreen()
|
||||||
|
elseif ev[1] == "trigger_update" then
|
||||||
|
pendingGateUpdate = true
|
||||||
elseif ev[1] == "timer" and ev[2] == versionTimer then
|
elseif ev[1] == "timer" and ev[2] == versionTimer then
|
||||||
pcall(function()
|
pcall(function()
|
||||||
refreshRemoteLuaVersion(guessBaseFromStatusURL(serverURL))
|
refreshRemoteLuaVersion(guessBaseFromStatusURL(serverURL))
|
||||||
end)
|
end)
|
||||||
if versionMismatch == true then
|
-- 取消自动标记更新,仅由外部事件(例如手动控制台)触发时处理
|
||||||
pendingGateUpdate = true
|
-- if versionMismatch == true then
|
||||||
elseif versionMismatch == false then
|
-- pendingGateUpdate = true
|
||||||
pendingGateUpdate = false
|
-- elseif versionMismatch == false then
|
||||||
end
|
-- pendingGateUpdate = false
|
||||||
|
-- end
|
||||||
if pendingGateUpdate and isGateIdleForUpdate() then
|
if pendingGateUpdate and isGateIdleForUpdate() then
|
||||||
|
isUpdating = true
|
||||||
|
drawReadyScreen()
|
||||||
|
os.sleep(0.5)
|
||||||
local ok, updated = pcall(runSilentGateUpdate)
|
local ok, updated = pcall(runSilentGateUpdate)
|
||||||
if ok and updated then
|
if ok and updated then
|
||||||
os.reboot()
|
os.reboot()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
isUpdating = false
|
||||||
|
pendingGateUpdate = false
|
||||||
end
|
end
|
||||||
drawReadyScreen()
|
drawReadyScreen()
|
||||||
versionTimer = os.startTimer(VERSION_CHECK_INTERVAL)
|
versionTimer = os.startTimer(VERSION_CHECK_INTERVAL)
|
||||||
|
|||||||
@@ -733,6 +733,7 @@ router.get('/tickets/:id', async (req, res, next) => {
|
|||||||
station_code: e.station_code || '',
|
station_code: e.station_code || '',
|
||||||
station_en: e.station_code ? enNameFor(e.station_code) : (e.station_en || ''),
|
station_en: e.station_code ? enNameFor(e.station_code) : (e.station_en || ''),
|
||||||
ticket_id: e.ticket_id,
|
ticket_id: e.ticket_id,
|
||||||
|
device: e.device || e.device_id || e.detail?.device || '',
|
||||||
start_name: nameFor(e.start),
|
start_name: nameFor(e.start),
|
||||||
terminal_name: nameFor(e.terminal),
|
terminal_name: nameFor(e.terminal),
|
||||||
train_type: e.train_type || '',
|
train_type: e.train_type || '',
|
||||||
|
|||||||
+21
-14
@@ -1,7 +1,7 @@
|
|||||||
local CURRENT_STATION_CODE = 'Ticket-Machine'
|
local CURRENT_STATION_CODE = 'Ticket-Machine'
|
||||||
local API_BASE = 'http://ticket.fse-media.group/api'
|
local API_BASE = 'http://ticket.fse-media.group/api'
|
||||||
local VERSION = 'v1.5.12'
|
local VERSION = 'v1.5.12'
|
||||||
local UPDATE_CHECK_INTERVAL = 60
|
local UPDATE_CHECK_INTERVAL = 5
|
||||||
|
|
||||||
-- ###########################
|
-- ###########################
|
||||||
-- Core HTTP & JSON Utilities
|
-- Core HTTP & JSON Utilities
|
||||||
@@ -15,6 +15,7 @@ end
|
|||||||
local serverConnected = nil
|
local serverConnected = nil
|
||||||
local versionMismatch = nil
|
local versionMismatch = nil
|
||||||
local pendingMachineUpdate = false
|
local pendingMachineUpdate = false
|
||||||
|
local isUpdating = false
|
||||||
local state
|
local state
|
||||||
|
|
||||||
local function normalizeVersionTag(v)
|
local function normalizeVersionTag(v)
|
||||||
@@ -662,21 +663,23 @@ local function runSilentMachineUpdate()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function backgroundSyncTask()
|
local function backgroundSyncTask()
|
||||||
|
pcall(refreshConfigOnce)
|
||||||
while true do
|
while true do
|
||||||
sleep(UPDATE_CHECK_INTERVAL)
|
|
||||||
pcall(refreshConfigOnce)
|
|
||||||
if versionMismatch == true then
|
|
||||||
pendingMachineUpdate = true
|
|
||||||
elseif versionMismatch == false then
|
|
||||||
pendingMachineUpdate = false
|
|
||||||
end
|
|
||||||
if pendingMachineUpdate and state and tostring(state.page or 'home') == 'home' then
|
if pendingMachineUpdate and state and tostring(state.page or 'home') == 'home' then
|
||||||
|
isUpdating = true
|
||||||
|
pcall(drawVersionIndicator)
|
||||||
|
sleep(0.5)
|
||||||
local ok, updated = pcall(runSilentMachineUpdate)
|
local ok, updated = pcall(runSilentMachineUpdate)
|
||||||
if ok and updated then
|
if ok and updated then
|
||||||
os.reboot()
|
os.reboot()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
isUpdating = false
|
||||||
|
pendingMachineUpdate = false
|
||||||
|
pcall(drawVersionIndicator)
|
||||||
end
|
end
|
||||||
|
sleep(UPDATE_CHECK_INTERVAL)
|
||||||
|
pcall(refreshConfigOnce)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -961,15 +964,15 @@ local function waitButtons()
|
|||||||
while true do
|
while true do
|
||||||
local ev, p1, p2, p3 = os.pullEvent()
|
local ev, p1, p2, p3 = os.pullEvent()
|
||||||
if ev == 'mouse_click' or ev == 'monitor_touch' then
|
if ev == 'mouse_click' or ev == 'monitor_touch' then
|
||||||
-- For mouse_click: p1=button, p2=x, p3=y
|
|
||||||
-- For monitor_touch: p1=side, p2=x, p3=y
|
|
||||||
for _, b in ipairs(Buttons) do
|
for _, b in ipairs(Buttons) do
|
||||||
if inRect(b, p2, p3) then
|
if inRect(b, p2, p3) then
|
||||||
clickSound(); if b.onClick then b.onClick() end; return
|
clickSound(); if b.onClick then b.onClick() end; return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
elseif ev == 'trigger_update' then
|
||||||
|
pendingMachineUpdate = true
|
||||||
elseif ev == 'config_updated' then
|
elseif ev == 'config_updated' then
|
||||||
-- Ignore config_updated while waiting for buttons, to prevent interrupting user's selection
|
if state and state.page == 'home' then return end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1103,7 +1106,11 @@ end
|
|||||||
local function drawVersionIndicator()
|
local function drawVersionIndicator()
|
||||||
if w < 1 then return end
|
if w < 1 then return end
|
||||||
local markerColor = colors.yellow
|
local markerColor = colors.yellow
|
||||||
if versionMismatch == true then
|
local markerText = '*'
|
||||||
|
if isUpdating then
|
||||||
|
markerColor = colors.yellow
|
||||||
|
markerText = '^ing'
|
||||||
|
elseif versionMismatch == true then
|
||||||
markerColor = colors.red
|
markerColor = colors.red
|
||||||
elseif versionMismatch == false then
|
elseif versionMismatch == false then
|
||||||
markerColor = colors.lime
|
markerColor = colors.lime
|
||||||
@@ -1112,9 +1119,9 @@ local function drawVersionIndicator()
|
|||||||
termDev.setTextColor(colors.gray)
|
termDev.setTextColor(colors.gray)
|
||||||
termDev.setCursorPos(1, 1)
|
termDev.setCursorPos(1, 1)
|
||||||
termDev.write(tostring(VERSION))
|
termDev.write(tostring(VERSION))
|
||||||
if w >= (#tostring(VERSION) + 1) then
|
if w >= (#tostring(VERSION) + #markerText) then
|
||||||
termDev.setTextColor(markerColor)
|
termDev.setTextColor(markerColor)
|
||||||
termDev.write('*')
|
termDev.write(markerText)
|
||||||
end
|
end
|
||||||
termDev.setTextColor(colors.white)
|
termDev.setTextColor(colors.white)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user