feat: 新增客户端问题主动上报与控制台问题汇总功能
1. 客户端 (ticketmachine.lua): 修改 showAlert 支持详细错误并主动上报,使用 pcall 包装主循环捕获 Fatal Error 并上报。 2. 服务端 (data.js, api.js): 增加 issues 数据表,提供 /report_issue 和 /issues 接口。 3. 控制台 (index.html, index.js): 添加“问题汇总”菜单及界面状态管理。
This commit is contained in:
+39
-10
@@ -327,6 +327,19 @@ local function enqueueTicketUpload(ticketData)
|
||||
return false
|
||||
end
|
||||
|
||||
local function reportIssue(errMsg, detail)
|
||||
local url = API_BASE .. '/report_issue'
|
||||
local payload = {
|
||||
device = currentDeviceId(),
|
||||
machine_id = currentDeviceId(),
|
||||
station_code = state.stationCode or CURRENT_STATION_CODE,
|
||||
error_msg = tostring(errMsg or ''),
|
||||
detail = tostring(detail or ''),
|
||||
ts = (os.epoch and os.epoch('utc')) or (os.time() * 1000)
|
||||
}
|
||||
postJSONResult(url, payload)
|
||||
end
|
||||
|
||||
-- ###########################
|
||||
-- Peripheral discovery
|
||||
-- ###########################
|
||||
@@ -1286,14 +1299,22 @@ local function renderConfirmCancel()
|
||||
addButton(bx+4+bw, by+4, 'NO', bw, 1, {colors.green, colors.white}, function() ui_cancel_confirmed = false; ui_cancel_request = false end)
|
||||
end
|
||||
|
||||
local function showAlert(msg)
|
||||
local function showAlert(msg, detail)
|
||||
if detail then
|
||||
reportIssue(msg, detail)
|
||||
end
|
||||
local boxW, boxH = math.max(24, math.min(32, w-4)), 6
|
||||
local bx, by = math.floor((w-boxW)/2)+1, math.floor((h-boxH)/2)+1
|
||||
termDev.setBackgroundColor(colors.gray)
|
||||
for i=0,boxH-1 do termDev.setCursorPos(bx, by+i); termDev.write(string.rep(' ', boxW)) end
|
||||
msg = tostring(msg or '')
|
||||
if #msg > boxW-4 then msg = msg:sub(1, boxW-4) end
|
||||
termDev.setTextColor(colors.white); termDev.setCursorPos(bx+2, by+2); termDev.write(msg)
|
||||
local displayMsg = tostring(msg or '')
|
||||
if #displayMsg > boxW-4 then displayMsg = displayMsg:sub(1, boxW-4) end
|
||||
termDev.setTextColor(colors.white); termDev.setCursorPos(bx+2, by+2); termDev.write(displayMsg)
|
||||
if detail then
|
||||
local displayDetail = tostring(detail)
|
||||
if #displayDetail > boxW-4 then displayDetail = displayDetail:sub(1, boxW-4) end
|
||||
termDev.setCursorPos(bx+2, by+3); termDev.write(displayDetail)
|
||||
end
|
||||
Buttons = {}
|
||||
addButton(bx+math.floor((boxW-6)/2), by+4, 'OK', 6, 1, {colors.green, colors.white}, function() end)
|
||||
waitButtons()
|
||||
@@ -1815,7 +1836,7 @@ local function showTerminal()
|
||||
invalidUntil = os.epoch('utc') + 1500
|
||||
sel = nil
|
||||
state.terminal = nil
|
||||
showAlert('Same station')
|
||||
showAlert('Same station', 'Dep and Term are identical')
|
||||
else
|
||||
state.terminal = sel
|
||||
end
|
||||
@@ -2073,7 +2094,7 @@ local function showOrderAndAudio()
|
||||
else
|
||||
local unit = computeCost(state.departure or '', state.terminal or '', state.trainType or 'Local')
|
||||
if not unit then
|
||||
showAlert('No route')
|
||||
showAlert('No route', 'Cannot find fare path')
|
||||
state.page = 'terminal'
|
||||
return
|
||||
end
|
||||
@@ -2084,7 +2105,7 @@ local function showOrderAndAudio()
|
||||
end
|
||||
|
||||
if state.productMode ~= 'card' and state.trainType ~= 'single' and sameLogicalStation(state.departure, state.terminal) then
|
||||
showAlert('Same station')
|
||||
showAlert('Same station', 'Cannot purchase to same station')
|
||||
state.page = state.voucher_code and 'online' or 'terminal'
|
||||
return
|
||||
end
|
||||
@@ -2097,7 +2118,7 @@ local function showOrderAndAudio()
|
||||
local function confirmAction()
|
||||
if processing or confirmed then return end
|
||||
if state.productMode ~= 'card' and sameLogicalStation(state.departure, state.terminal) then
|
||||
showAlert('Same station')
|
||||
showAlert('Same station', 'Dep and Term are identical on confirm')
|
||||
if render then render() end
|
||||
return
|
||||
end
|
||||
@@ -2411,7 +2432,7 @@ local function showDone()
|
||||
local issueError = tostring(issueMethod or 'ticket_issue_failed')
|
||||
print('Ticket issue failed: ' .. issueError)
|
||||
_G.TICKET_MACHINE_LAST_TICKET.ticket_issue_error = issueError
|
||||
showAlert('Ticket issue failed')
|
||||
showAlert('Ticket issue failed', issueError)
|
||||
resetTicketFlow()
|
||||
state.page = 'home'
|
||||
return
|
||||
@@ -2608,4 +2629,12 @@ local function mainPageLoop()
|
||||
end
|
||||
end
|
||||
|
||||
parallel.waitForAny(mainPageLoop, backgroundSyncTask, backgroundTicketUploadTask, backgroundPeripheralTask)
|
||||
local function safeMain()
|
||||
local ok, err = pcall(parallel.waitForAny, mainPageLoop, backgroundSyncTask, backgroundTicketUploadTask, backgroundPeripheralTask)
|
||||
if not ok then
|
||||
reportIssue("Fatal Error", tostring(err))
|
||||
error(err)
|
||||
end
|
||||
end
|
||||
|
||||
safeMain()
|
||||
|
||||
Reference in New Issue
Block a user