From b3fdf36f8405732c9fe33160cb06598c5a934266 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Thu, 9 Jul 2026 23:40:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(pricing):=20=E7=BB=9F=E4=B8=80=E5=B0=86?= =?UTF-8?q?=E8=AE=A1=E4=BB=B7=E8=AE=A1=E7=AE=97=E4=BB=8E=E5=90=91=E4=B8=8B?= =?UTF-8?q?=E5=8F=96=E6=95=B4=E6=94=B9=E4=B8=BA=E5=9B=9B=E8=88=8D=E4=BA=94?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一修复所有计价相关模块的取整逻辑:将Lua计价函数中的直接返回改为四舍五入取整,同时将前后端代码中的Math.floor替换为Math.round,确保全链路的票价计算采用标准四舍五入规则,避免出现不合理的定价偏差。 --- old-ticket-order.js | Bin 57072 -> 28417 bytes server/routes/api.js | 18 +++++++++++++++--- server/routes/public.js | 11 +++++++++-- server/services/logic.js | 2 +- ticketmachine.lua | 2 +- web/ticket-order.js | 2 +- 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/old-ticket-order.js b/old-ticket-order.js index 4986de31e136cb4ba021c8b990da020a5008efcc..0000aa029fde4c309743dcd66437d4c6e6702e9b 100644 GIT binary patch literal 28417 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 (req.headers['x-forwarded-for'] || '').toString().split(',')[0].trim() || req.ip || req.connection?.remoteAddress || ''; +const resolveStationName = (code) => { + if (!code) return ''; + const s = (DataService.getStations?.() || []).find(x => String(x.code).trim().toLowerCase() === String(code).trim().toLowerCase()); + return s ? (s.name || s.cn_name || code) : code; +}; + const normalizeTicketId = (v) => { const s0 = String(v || '').replace(/\s+/g, ''); if (!s0) return ''; @@ -829,6 +835,7 @@ router.post('/ic-cards/:id/sync', async (req, res) => { result: patch.last_result, reason: patch.last_reason || undefined, station_code: station_code || undefined, + station_name: resolveStationName(station_code), entry_station: card.entry_station || undefined, exit_station: card.exit_station || undefined, fare: patch.last_fare, @@ -916,6 +923,7 @@ router.post('/cards/open', async (req, res) => { card_id: card.card_id, order_code: voucher_code, station_code, + station_name: resolveStationName(station_code), device, balance, deposit, @@ -1067,6 +1075,7 @@ router.post('/cards/check', async (req, res) => { action, result: 'pass', station_code, + station_name: resolveStationName(station_code), entry_station: station_code, device, balance: toMoney(card.balance) @@ -1130,6 +1139,7 @@ router.post('/cards/check', async (req, res) => { action, result: 'pass', station_code, + station_name: resolveStationName(station_code), entry_station, exit_station: station_code, fare, @@ -1186,6 +1196,7 @@ router.post('/tickets/sale', async (req, res) => { train_type, cost: cost || 0, station_code: station_code || '', + station_name: resolveStationName(station_code), device: device || 'unknown', trips_total: tTotal, trips_remaining: tRemain @@ -1277,7 +1288,7 @@ router.post('/tickets/check', async (req, res) => { const cur = idx[ticket_id]; const deny = async (reason) => { - const ev = { ts, type: 'check', ticket_id, action, result: 'deny', reason, station_code: station_code_raw || '', device }; + const ev = { ts, type: 'check', ticket_id, action, result: 'deny', reason, station_code: station_code_raw || '', station_name: resolveStationName(station_code_raw), device }; await DataService.appendTicketEvent(ev); await DataService.upsertTicketIndex({ ticket_id, @@ -1329,7 +1340,7 @@ router.post('/tickets/check', async (req, res) => { if (!matchesStation(start)) return deny('wrong_station'); if (entered && !exited) return deny('already_entered'); - const ev = { ts, type: 'check', ticket_id, action, result: 'pass', station_code: start, device }; + const ev = { ts, type: 'check', ticket_id, action, result: 'pass', station_code: start, station_name: resolveStationName(start), device }; await DataService.appendTicketEvent(ev); await DataService.upsertTicketIndex({ ticket_id, @@ -1355,7 +1366,7 @@ router.post('/tickets/check', async (req, res) => { const newTripsRemaining = Math.max(0, (Number(tripsRemainEffective) || 0) - 1); const shouldSetUsed = newTripsRemaining <= 0; - const ev = { ts, type: 'check', ticket_id, action, result: 'pass', station_code: terminal, device, trips_remaining: newTripsRemaining }; + const ev = { ts, type: 'check', ticket_id, action, result: 'pass', station_code: terminal, station_name: resolveStationName(terminal), device, trips_remaining: newTripsRemaining }; await DataService.appendTicketEvent(ev); await DataService.upsertTicketIndex({ ticket_id, @@ -1399,6 +1410,7 @@ router.post('/tickets/status', async (req, res) => { result, reason, station_code: station_code || '', + station_name: resolveStationName(station_code), device: device || 'unknown', trips_remaining, rides_remaining diff --git a/server/routes/public.js b/server/routes/public.js index 7862d83..5ecbf55 100644 --- a/server/routes/public.js +++ b/server/routes/public.js @@ -9,6 +9,12 @@ const svgGenerator = require('../services/svg-generator'); // Helper to get IP const getIp = (req) => (req.headers['x-forwarded-for'] || '').toString().split(',')[0].trim() || req.ip || req.connection?.remoteAddress || ''; +const resolveStationName = (code) => { + if (!code) return ''; + const s = (DataService.getStations?.() || []).find(x => String(x.code).trim().toLowerCase() === String(code).trim().toLowerCase()); + return s ? (s.name || s.cn_name || code) : code; +}; + const appendReqLog = (req, { category, type, detail, source, level } = {}) => { const entry = { ts: new Date().toISOString(), @@ -196,8 +202,8 @@ router.get('/fares/query', (req, res) => { const discount = Number.isFinite(discountRaw) && discountRaw > 0 ? discountRaw : 1; const regularBase = Number(result.regular); const expressBase = Number(result.express); - const regularDiscounted = Number.isFinite(regularBase) ? Math.floor(regularBase * discount) : null; - const expressDiscounted = Number.isFinite(expressBase) ? Math.floor(expressBase * discount) : null; + const regularDiscounted = Number.isFinite(regularBase) ? Math.round(regularBase * discount) : null; + const expressDiscounted = Number.isFinite(expressBase) ? Math.round(expressBase * discount) : null; appendReqLog(req, { category: 'public', type: 'fare_query', detail: { from, to, from_code: fromCode, to_code: toCode, ok: true } }); res.json({ from_code: fromCode || null, @@ -502,6 +508,7 @@ router.post('/tickets/record', async (req, res) => { train_type, cost, station_code, + station_name: resolveStationName(station_code), device, trips_total, trips_remaining diff --git a/server/services/logic.js b/server/services/logic.js index 98a092e..6c20afd 100644 --- a/server/services/logic.js +++ b/server/services/logic.js @@ -215,7 +215,7 @@ const LogicService = { const baseExp = Number(baseBoth?.express ?? 0) || 0; const discount = Number(cfg?.promotion?.discount ?? 1); const base = (trainType === 'Express') ? baseExp : baseReg; - return Math.floor(base * (discount > 0 ? discount : 1)); + return Math.round(base * (discount > 0 ? discount : 1)); } catch (_) { return 0; } }, diff --git a/ticketmachine.lua b/ticketmachine.lua index 64e49b4..875bc32 100644 --- a/ticketmachine.lua +++ b/ticketmachine.lua @@ -1644,7 +1644,7 @@ local function computeCost(src, dst, trainType) local u, du = heapPop() if not u then break end if du == dist[u] then - if u == dst then return du end + if u == dst then return math.floor(du + 0.5) end for v, w in pairs(adj[u] or {}) do local nd = du + (tonumber(w) or 0) if nd < (dist[v] or math.huge) then diff --git a/web/ticket-order.js b/web/ticket-order.js index c9652cc..df32b01 100644 --- a/web/ticket-order.js +++ b/web/ticket-order.js @@ -567,7 +567,7 @@ const discountedRaw = (type==='Express') ? (fare?.discounted_express_fare ?? fare?.['优惠后特快票价']) : (fare?.discounted_regular_fare ?? fare?.['优惠后常规票价']); - const discountedSingle = Number(discountedRaw ?? Math.floor(base * disc) ?? 0); + const discountedSingle = Number(discountedRaw ?? Math.round(base * disc) ?? 0); const price = discountedSingle * trips; const routeKey = (type === 'Express') ? 'express_path' : 'regular_path';