diff --git a/gate.lua b/gate.lua index dabefc5..c4547de 100644 --- a/gate.lua +++ b/gate.lua @@ -1520,26 +1520,27 @@ local function backgroundSyncTask() end local function backgroundLocationSyncTask() - while true do - if gps and type(gps.locate) == "function" then - local x, y, z = gps.locate(2) - if x and y and z then - local label = os.getComputerLabel() - if not label or label == "" then label = "#" .. tostring(os.getComputerID()) end - local payload = { - device_id = label, - type = "Gate", - gate_mode = modeBySide and "bidirectional" or mode, - station_code = stationCodeForSide(nil) or "", - x = x, - y = y, - z = z - } - local serverBase = guessBaseFromStatusURL(serverURL) - pcall(postCheck, serverBase .. '/api/machine-location', payload) - end + sleep(5) + if gps and type(gps.locate) == "function" then + local x, y, z = gps.locate(2) + if x and y and z then + local label = os.getComputerLabel() + if not label or label == "" then label = "#" .. tostring(os.getComputerID()) end + local payload = { + device_id = label, + type = "Gate", + gate_mode = modeBySide and "bidirectional" or mode, + station_code = stationCodeForSide(nil) or "", + x = x, + y = y, + z = z + } + local serverBase = guessBaseFromStatusURL(serverURL) + pcall(postCheck, serverBase .. '/api/machine-location', payload) end - sleep(60) + end + while true do + sleep(86400) end end diff --git a/server/routes/api.js b/server/routes/api.js index 7140159..93773d2 100644 --- a/server/routes/api.js +++ b/server/routes/api.js @@ -163,27 +163,31 @@ router.get('/check-update', (req, res) => { res.json({ update_counter: globalUpdateCounter }); }); -router.post('/machine-location', (req, res) => { +router.post('/machine-location', async (req, res) => { const data = req.body || {}; if (!data.device_id) return res.status(400).json({ ok: false, error: 'device_id required' }); if (!data.station && data.station_code) { data.station = resolveStationName(data.station_code); } - DataService.updateMachineLocation(data.device_id, data); + await DataService.updateMachineLocation(data.device_id, data); res.json({ ok: true }); }); router.get('/machine-locations', (req, res) => { const locs = DataService.getMachineLocations() || {}; - // clean up old locations (> 5 minutes) - const now = Date.now(); - const active = {}; - for (const [id, data] of Object.entries(locs)) { - if (now - data.last_update_ts < 5 * 60 * 1000) { - active[id] = data; - } + res.json({ ok: true, data: locs }); +}); + +router.put('/machine-location/:id', async (req, res) => { + const id = req.params.id; + const data = req.body || {}; + const current = DataService.getMachineLocations()[id]; + if (!current) return res.status(404).json({ ok: false, error: 'machine not found' }); + if (!data.station && data.station_code) { + data.station = resolveStationName(data.station_code); } - res.json({ ok: true, data: active }); + await DataService.updateMachineLocation(id, data); + res.json({ ok: true }); }); // Config diff --git a/server/routes/public.js b/server/routes/public.js index 311c0ae..2d1a13a 100644 --- a/server/routes/public.js +++ b/server/routes/public.js @@ -224,6 +224,11 @@ router.get('/fares/query', (req, res) => { } }); +router.get('/machine-locations', (req, res) => { + const locs = DataService.getMachineLocations() || {}; + res.json({ ok: true, data: locs }); +}); + router.get('/config', (req, res) => { const cfg = DataService.getConfig(); res.json({ diff --git a/server/services/data.js b/server/services/data.js index b1df111..a405ba3 100644 --- a/server/services/data.js +++ b/server/services/data.js @@ -95,6 +95,9 @@ const DataService = { if (configs.length > 0) cache.config = normalizeConfig(configs[0].v); else await conn.query('INSERT INTO kv_store (k, v) VALUES (?, ?)', ['config', JSON.stringify(cache.config)]); + const [machLocs] = await conn.query('SELECT v FROM kv_store WHERE k = ?', ['machine_locations']); + if (machLocs.length > 0) cache.machineLocations = machLocs[0].v || {}; + const [stations] = await conn.query('SELECT data FROM stations'); cache.stations = stations.map(r => r.data); @@ -526,11 +529,16 @@ const DataService = { }, getMachineLocations: () => cache.machineLocations, - updateMachineLocation: (deviceId, data) => { + updateMachineLocation: async (deviceId, data) => { cache.machineLocations[deviceId] = { + ...cache.machineLocations[deviceId], ...data, last_update_ts: Date.now() }; + try { + await pool.query('INSERT INTO kv_store (k, v) VALUES (?, ?) ON DUPLICATE KEY UPDATE v = VALUES(v)', + ['machine_locations', JSON.stringify(cache.machineLocations)]); + } catch(e) { console.error('updateMachineLocation error', e); } } }; diff --git a/ticketmachine.lua b/ticketmachine.lua index 3602cf4..2ef5f7b 100644 --- a/ticketmachine.lua +++ b/ticketmachine.lua @@ -776,23 +776,24 @@ local function backgroundTicketUploadTask() end local function backgroundLocationSyncTask() - while true do - if gps and type(gps.locate) == "function" then - local x, y, z = gps.locate(2) - if x and y and z then - local payload = { - device_id = currentDeviceId(), - type = "TicketMachine", - station = (CFG.current_station and (CFG.current_station.en_name or CFG.current_station.name)) or state.stationName, - station_code = state.stationCode or CURRENT_STATION_CODE, - x = x, - y = y, - z = z - } - postJSONResult(API_BASE .. '/machine-location', payload) - end + sleep(5) + if gps and type(gps.locate) == "function" then + local x, y, z = gps.locate(2) + if x and y and z then + local payload = { + device_id = currentDeviceId(), + type = "TicketMachine", + station = (CFG.current_station and (CFG.current_station.en_name or CFG.current_station.name)) or state.stationName, + station_code = state.stationCode or CURRENT_STATION_CODE, + x = x, + y = y, + z = z + } + postJSONResult(API_BASE .. '/machine-location', payload) end - sleep(60) + end + while true do + sleep(86400) end end diff --git a/web/home.html b/web/home.html index 8e6be64..bea0eea 100644 --- a/web/home.html +++ b/web/home.html @@ -39,6 +39,7 @@ 车票查询 IC 卡查询 线上购卡 + 机器位置 diff --git a/web/ic-card-order.html b/web/ic-card-order.html index 937c815..299b47b 100644 --- a/web/ic-card-order.html +++ b/web/ic-card-order.html @@ -40,6 +40,7 @@ 车票查询 IC 卡查询 线上购卡 + 机器位置 diff --git a/web/ic-card-search.html b/web/ic-card-search.html index 296b4ad..ec4258b 100644 --- a/web/ic-card-search.html +++ b/web/ic-card-search.html @@ -40,6 +40,7 @@ 车票查询 IC 卡查询 线上购卡 + 机器位置 diff --git a/web/index.html b/web/index.html index 658581e..f965989 100644 --- a/web/index.html +++ b/web/index.html @@ -686,6 +686,9 @@
站点: {{ loc.station || loc.station_code || '未知' }}
坐标: {{ loc.x }}, {{ loc.y }}, {{ loc.z }}
+
+ +
@@ -693,6 +696,47 @@ + +
+
+
+

编辑设备位置 - {{ editMachineForm.device_id }}

+ +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
+
diff --git a/web/index.js b/web/index.js index 9f07b43..09f2682 100644 --- a/web/index.js +++ b/web/index.js @@ -1165,6 +1165,36 @@ createApp({ } }; + const showEditMachineModal = ref(false); + const editMachineForm = reactive({ device_id: '', station_code: '', x: 0, y: 0, z: 0, type: 'TicketMachine' }); + + const openEditMachineModal = (id) => { + const loc = machineLocations.value[id]; + if (!loc) return; + editMachineForm.device_id = id; + editMachineForm.station_code = loc.station_code || loc.station || ''; + editMachineForm.x = loc.x || 0; + editMachineForm.y = loc.y || 0; + editMachineForm.z = loc.z || 0; + editMachineForm.type = loc.type || 'TicketMachine'; + showEditMachineModal.value = true; + }; + + const saveMachineLocation = async () => { + try { + const payload = { ...editMachineForm }; + await requestJson(`/api/machine-location/${encodeURIComponent(payload.device_id)}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }, { expectOk: true }); + showEditMachineModal.value = false; + await fetchMachineLocations(); + } catch (e) { + alert('保存失败: ' + e.message); + } + }; + const ensureViewData = async (view = currentView.value, { force = false } = {}) => { await fetchCoreData({ force }); if (view === 'tickets' && (force || !ticketDataLoaded)) await fetchTicketData(); @@ -1780,7 +1810,7 @@ createApp({ }; return { - machineLocations, selectedMachineId, getMapX, getMapY, + machineLocations, selectedMachineId, getMapX, getMapY, showEditMachineModal, editMachineForm, openEditMachineModal, saveMachineLocation, currentView, viewTitle, sidebarOpen, loadingState, isViewBusy, lastSyncText, currentViewSummary, stations, lines, fares, stats, config, recentLogs, ticketList, ticketStats, orderStats, diff --git a/web/machine-map.html b/web/machine-map.html new file mode 100644 index 0000000..ed63846 --- /dev/null +++ b/web/machine-map.html @@ -0,0 +1,167 @@ + + + + + + + 机器位置|FSE铁路运输票务系统 + + + + + + +
+
+
+ + + 返回首页 + +
+ 服务器状态 + + 检测中 +
+
+
+ + + +
+
+ MACHINE MAP +

设备分布图

+

实时查看所有售票机和检票机的位置及状态。

+
+ +
+
+

机器位置

+ Live Map +
+
+
+ 加载中... +
+
+
+
+
+ + + + diff --git a/web/ticket-order.html b/web/ticket-order.html index 6e435da..5e77868 100644 --- a/web/ticket-order.html +++ b/web/ticket-order.html @@ -38,9 +38,10 @@ 首页 线上预定 车票查询 - IC 卡查询 - 线上购卡 - + IC 卡查询 + 线上购卡 + 机器位置 +
diff --git a/web/ticket-search.html b/web/ticket-search.html index 3530f63..4d1fe47 100644 --- a/web/ticket-search.html +++ b/web/ticket-search.html @@ -40,7 +40,8 @@ 线上预定 车票查询 IC 卡查询 - 线上购卡 + 线上购卡 + 机器位置