122 KiB
IC储蓄卡功能开发
Workspace: d:\TaoYuan2023_HSTG\HSTG\Lua\TicketMachine
User
现在我们新增了一个IC储蓄卡的功能。MOD那边的API是这样的: IC 卡系统 Lua API
售票机 ticket_vending_machine 新增
方法 | 返回
issueTicket(...) | true, ticketId
issueICCard() | true, cardId
local ok, cardId = vm.issueICCard()
充值机 ic_refill_machine 方法 | 参数 | 返回 | 说明 getCardInfo() | 无 | table |卡号/持有者/余额 refill(amount) | 金额 | true, 新余额 | 充值 deduct(amount) | 金额 | true, 新余额 | 扣费
操作流程:手持卡右键方块 → 插入 → CC 操作 → ejectCard() 取出
所以要加:
-
d:\TaoYuan2023_HSTG\HSTG\Lua\TicketMachine\ticketmachine.lua添加一个“办卡”的页面。点击按钮后开始办卡。首先输入卡的使用者。输入框下面有26键键盘以及符号键盘。确定之后选择首次充值余额。可以自定义,也可以选择预设:5、10、15、20。然后选择之后就像“购票支付”d:\TaoYuan2023_HSTG\HSTG\Lua\TicketMachine\ticketmachine.lua#L902-902那样付钱充值.完成之后就出卡。把使用者名、余额传入出票机。同时也要向网页端记录该卡。 -
检票模块: 卡右键检票机,向卡中传入进站的车站编号。出站时通过识别卡中的进站站台编号和当前出站站台编号,计算路费。计算后扣费后向卡中传入新的余额。
其它“已经进站”/“还未进站”等提示照旧
3.网页控制台端。加上新的记录卡信息的/api 地址。然后后台也能看到卡的所有信息(类似车票)。同时也要在公开的页面显示,加入查询的索引。
- 线上购卡 和线上购票类似,通过线上购卡,然后获得凭证,在computercraft中通过凭证兑卡。
请注意,所有网页的css请按照当前的css风格(模仿JR官网 https://www.jreast.co.jp/ )
Assistant
我先梳理现有代码结构,重点看 ticketmachine.lua 的购票支付流程、检票模块的进出站逻辑,以及网页端现有的票据 API 和展示页,确认新加 IC 储蓄卡最合适的接入点。
我已做了完整检索,覆盖了售票机主程序、线上预定页、凭证详情页,以及闸机核验侧代码;下面按“页面结构、输入交互、支付、出票、接入点”汇总。
主流程
- 主状态机在 mainPageLoop,页面顺序是
home -> departure -> terminal -> type -> trips -> order -> done,线上兑票走home -> online -> order -> done - 首页模式选择在 showHome,只有两个入口:
NEW新购票、ONLINE凭证兑票 - 站点/线路选择页共用 renderLinesSelection,数据来自
CFG.lines / CFG.stations - 订单确认与支付页在 showOrderAndAudio,出票完成页在 showDone
- 线上凭证输入页在 showOnlineVoucher
页面结构
home:模式选择页,按钮为NEW / ONLINE,进入不同购票链路,见 showHomedeparture:选择出发站,见 showDepartureterminal:选择到达站,禁止同站,见 showTerminaltype:选择Local / Express,见 showTypetrips:选择乘次数量,+/-调整,见 showTripsorder:显示区间、车型、次数、金额、支付进度条、确认按钮,见 drawOrder 与 showOrderAndAudiodone:真正执行出票/上传后展示票据信息,见 showDoneonline:5 位凭证码输入页,见 showOnlineVoucher
输入交互
- 统一按钮机制在 addButton / waitButtons,支持
mouse_click和monitor_touch - 全局取消弹窗在 renderConfirmCancel,各页通过
addCancelButton接入 - 站点选择通过触摸按钮完成,支持滚动翻页,见 renderLinesSelection
- 线上凭证支持两种输入:屏幕软键盘、实体键盘
char/backspace/enter,见 showOnlineVoucher - 金额不是文本输入,而是监听右侧红石输入脉冲累加,见 showOrderAndAudio
支付流程
- 票价计算核心在 computeCost,基于
CFG.fares构造普通/特急邻接图后跑最短路 - 订单页会先重算价格,避免旧数据残留,见 showOrderAndAudio
- 支付状态 UI 在 drawOrder,显示
Paid / Remaining与进度条 - 实际“收款”逻辑很简单:每次
redstone.getInput('right')从false -> true,state.paid += 1,见 showOrderAndAudio - 付满后自动确认;若金额为 0 也会自动确认,见 confirmAction 与 auto-confirm
- 当前代码没有接现金/扫码/银行卡 API;支付接入本质上就是把外设回调换成更新
state.paid,或重写确认条件
出票流程
- 本地新购票:支付满足后
confirmed=true,跳到done,见 showOrderAndAudio - 线上凭证兑票:确认时先 POST
/public/orders/{code}/consume,成功后才进入done,见 consume voucher - 真正出票发生在 showDone:优先调用外设
ticket_vending_machine.issueTicket(...) - 若外设调用失败,则退化为本地生成票号 generateTicketId
- 出票后会组装
ticketDataMod并上报/tickets/sale,见 showDone 与 uploadTicketRecord - 上传失败会写入
logs/pending_ticket_upload.jsonl,后台任务自动补传,见 enqueueTicketUpload 和 backgroundTicketUploadTask
线上预定关联文件
- 预定页面结构: ticket-order.html
- 预定页逻辑: ticket-order.js 与 createOrder
- 凭证详情页结构: token.html
- 凭证详情页逻辑: token.js
- Web 端创建凭证接口:
POST /api/public/orders,见 ticket-order.js - 机台兑票查询接口:
GET /public/orders/{code},见 showOnlineVoucher
建议接入点
- 支付外设接入:优先改 showOrderAndAudio,把
redstone right替换为投币器/纸币器/扫码回调,再统一累加到state.paid - 支付校验扩展:改 confirmAction,可在确认前增加第三方支付结果查询、超时撤单、退款判断
- 出票设备接入:改 showDone,当前已预留
ticket_vending_machine.issueTicket(...),这是最直接硬件接入点 - 售票上报接入:改 uploadTicketRecord,适合补充订单号、支付渠道、设备编号、流水号
- 线上订单联动:改 showOnlineVoucher.submitCode 与 consume voucher,适合加预锁定、核销前校验、重复兑票保护
- 闸机验票联动:下游在 gate.lua,用于票据进出站核验,适合作为出票后全链路闭环入口
结论
ticketmachine.lua已具备完整页面状态机、触摸/键盘交互、基础支付累加、出票外设调用、售票上传与离线补传- 当前最薄弱的部分是“支付”,本质仍是右侧红石脉冲计数,不是真正支付网关
- 最推荐的三个接入点是:
showOrderAndAudio的支付监听、confirmAction的核销确认、showDone的issueTicket外设调用
如果你需要,我下一步可以继续把这些流程整理成一张“页面/状态/事件/接口”的接入表。
已做一轮完整检索,覆盖 gate.lua、售票机 Lua、安装脚本,以及与闸机校验直接对应的服务端路由;结论是:当前 Lua 侧已经有比较清晰的“票卡介质适配层”,可直接扩展为 IC 卡接入。
结论
- 最核心接入点在 gate.lua:闸机不直接操作软盘文件,而是依赖
ticket_inspection_machine外设的事件和方法,这里最适合替换/扩展为 IC 卡读写器。 - 售票/发卡接入点在 ticketmachine.lua:售票机通过
ticket_vending_machine.issueTicket(...)下发票据,可平移为“写 IC 卡初始化数据/发新卡”。 - 进出站状态维护分两层:Lua 侧只做事件接收、服务器请求、回写介质;真正的状态判定和扣次在服务端 api.js。
- 如果要支持 IC 卡,优先保留现有 HTTP 校验协议不变,只替换 Lua 外设层,改造成本最低。
进出站状态
- 闸机模式来自 loadConfig,支持单向
mode=entry/exit,也支持双向side_modes,见 gate.lua:L422-L471。 - 扫描事件入口是 ticket_scanned,参数解析在 parseTicketScannedArgsPacked。
- 进/出站动作选择在 handleScan:优先按闸机侧别决定,双向且无侧别时会根据票内
entered/exited推断当前是进站还是出站。 - 票状态字段明确使用
entered、exited、trips_total、trips_remaining,见 gate.lua:L559-L600 和 ticketmachine.lua:L1265-L1279。
票据读写
- 读票不是 Lua 自己读文件,而是等待外设抛出
ticket_scanned事件,并从事件里拿scan表,见 gate.lua:L530-L545。 - 票号读取统一走 getTicketId,兼容
ticketId/ticket_id/id多种字段。 - 起终点读取统一走 getStartStation,兼容多种字段名,也支持从站名反推站码。
- 验票通过后,Lua 侧对介质回写在 gate.lua:L632-L659:
updateTicket("entered"/"exited"/"rides", ...),以及必要时destroyTicket()。 - 售票写票在 ticketmachine.lua:L1236-L1249:调用
ticket_vending_machine.issueTicket(...),这是发新票/初始化卡片的现成接入点。
扣费与校验
gate.lua自己不做本地票价计算,也不直接扣费;它只向/api/tickets/check提交ticket_id + action + station_codes + trips_*,见 postCheck 与 doCheck。- 服务端才是权威校验点:进站校验起点站、是否已进站;出站校验终点站、是否已进站/已出站,并在出站时
trips_remaining - 1,见 api.js:L650-L697。 - 服务端返回
resp.trips_remaining与resp.destroy_ticket,Lua 再把剩余次数写回票介质,见 gate.lua:L632-L659。 - 售票后会把票据记录上传到后端,见 enqueueTicketUpload 与 ticketmachine.lua:L1308-L1310。
可扩展为 IC 卡的接入点
ticket_inspection_machine外设接口是第一接入点,见 gate.lua:L479-L493;只要 IC 卡读卡器实现:- 抛出
ticket_scanned事件,并提供一个scan表,至少含ticket_id/id,最好再带entered/exited/trips_remaining/start_station/terminal_station。 - 提供
updateTicket(key, value),用于进出站后回写卡内状态。 - 提供
destroyTicket(),用于单次票或次数耗尽后的废票/锁卡处理。 ticket_vending_machine.issueTicket(...)是第二接入点,见 ticketmachine.lua:L1236-L1249;可替换成 IC 发卡器,把新票初始数据写入空卡。- 安装配置层已支持闸机模式和站点下发,见 installer.lua 与 installer_bi.lua;无需改协议,只需新增 IC 设备部署说明。
建议方案
- 最小改造:保留 gate.lua 与后端
/tickets/check协议不变,实现一个兼容ticket_inspection_machine的 IC 卡外设。 - 如果 IC 卡要做脱机校验,可把 handleScan 中
doCheck()前后拆成“本地预校验 + 在线确认 + 回写卡片”三段。 - 如果要支持储值卡而不是次数票,新增点应放在服务端
/tickets/check与售票记录模型,不建议把扣费逻辑直接塞进 Lua。 - 当前代码最像“票卡介质抽象层”的其实不是文件读写,而是这两个外设接口:
ticket_inspection_machine和ticket_vending_machine。
如果你要,我下一步可以直接给你整理一份“IC 卡外设最小接口协议”,按这个项目现有 Lua 调用方式列出函数签名和事件格式。
我已做过一轮较完整的代码检查,覆盖了管理控制台、公开查询/预定页、后端路由、MySQL 持久化,以及售票机侧的线上兑票流程;结论如下。
现状总览
- 网页控制台入口是 index.html,已包含“车票记录”“凭证管理”等视图。
- 控制台前端逻辑在 index.js;其数据拉取集中在 index.js:L546-L560,已直接读取
/api/tickets、/api/orders。 - 公开查询页是 ticket-search.html + ticket-search.js,调用
/api/public/tickets与/api/public/tickets/:id。 - 线上预定/凭证页是 ticket-order.js 和 token.js,调用
/api/public/orders、/api/public/orders/:code。 - 路由挂载统一在 server/index.js;公开页面 URL 在 server/index.js:L66-L87。
- 数据存储不是 README 里旧的
web/data/*.json,而是 MySQL;核心实现是 data.js 和建表逻辑 data.js:L47-L60。
当前已存在的票据/兑票结构
- 票据主索引:
ticket_index,缓存字段见 data.js:L35-L39 与 data.js:L335-L375。 - 票据事件流:
ticket_events,写入/读取见 data.js:L312-L333。 - 线上预定凭证:
orders+order_index,创建/查询/消费在 public.js:L109-L143、public.js:L146-L176、public.js:L178-L215。 - 售票机已支持“ONLINE”凭证兑票:入口在 ticketmachine.lua:L693-L731,凭证输入与校验在 ticketmachine.lua:L1338-L1424,支付确认后调用消费接口在 ticketmachine.lua:L1057-L1074。
- 售票后会把票据上传到
/api/tickets/sale,离线失败写本地队列logs/pending_ticket_upload.jsonl,见 ticketmachine.lua:L192-L259。
IC 卡现状
- 项目里没有独立的 “IC 卡记录 / IC 卡索引 / IC 卡事件” 后端结构。
- 现有模型只有“纸票/电子票 ticket”与“线上凭证 order”;也就是线上兑票已经有,但“兑卡”还没有独立数据层。
新增 IC 卡记录与线上兑卡,最少需要的文件
- 后端存储扩展:修改 data.js
- 管理端 API:修改 api.js
- 公开端 API:修改 public.js
- 控制台页面:修改 index.html 与 index.js
- 若要给用户公开查卡/看兑卡凭证:复用或新增 token.html、token.js
- 若售票机直接兑卡:修改 ticketmachine.lua
建议新增的数据结构
ic_cardscard_idphysical_uidstatus(active/lost/disabled)balancedepositholderbind_order_codecreated_tsupdated_ts
ic_card_eventscard_idtype(issue/topup/redeem/entry/exit/refund/disable)station_codedeviceamountticket_idorder_codets
ic_order_index或复用orders- 若复用现有
orders,建议加字段: fulfill_type: 'ticket' | 'ic_card'target_card_idredeem_statusredeemed_ts
- 若复用现有
建议新增的 API 结构
- 管理端
GET /api/ic-cardsGET /api/ic-cards/:idPOST /api/ic-cardsPUT /api/ic-cards/:idGET /api/ic-cards/:id/events
- 公开端
POST /api/public/ic-orders创建线上兑卡订单GET /api/public/ic-orders/:code查询兑卡凭证POST /api/public/ic-orders/:code/redeem售票机/制卡机兑卡
- 设备端
- 若沿用现有订单体系,也可直接在
orders上扩展redeem_target='ic_card'
- 若沿用现有订单体系,也可直接在
落地建议
- 最稳妥的做法是:不要硬改现有
ticket_index;新增ic_cards、ic_card_events两套表。 - 线上兑卡优先复用现有
orders/order_index流程,只给订单加fulfill_type='ic_card'和兑卡结果字段。 - 控制台可在现有 “凭证管理” 基础上增加 “IC卡记录” 视图,避免再开一套后台框架。
- 售票机侧已具备线上凭证输入/消费骨架,新增“兑卡”只需要在 ticketmachine.lua:L1338-L1424 的成功分支里把目标从“出票”改为“写卡/绑卡”。
如果你要,我下一步可以直接按这个检查结果,继续给你整理一份“IC 卡功能改造清单(逐文件到字段级)”。
现有结构已经比较清楚了:Lua 侧有成熟的页面状态机、支付与出票上传链路,后端也有票据与线上凭证模型。接下来我会直接读关键文件,把 IC 卡需要复用和新增的字段、页面、API 精确到函数级,再开始落代码。
我改成分段读取,避免一次性拉太多文件导致 IDE 超时。先把将要修改的函数和路由精确定位出来,再逐段展开读取。
我已经锁定了关键函数和路由位置,现在开始逐段读取并准备改动方案。先从 ticketmachine.lua 的页面流和 data.js 的存储接口入手,因为这两处会决定前后端字段是否能对齐。
读取工具对这些大文件持续超时,我改用终端按行读取,并顺手检查工作区当前改动,避免误覆盖你已有的内容。
这里有一个关键不确定点:你给的 MOD API 里只明确了 issueICCard() 和充值机 getCardInfo/refill/deduct,但没有看到“在办卡时写入持有者名/初始余额”以及“检票机读写进站站号”的具体 Lua 接口。
我先继续把现有代码读细,确认哪些地方可以先做;如果最终缺少 MOD 侧读写接口,我会单独把那一处列出来请你补一下函数名。
我继续往前做,不过会把缺失的 MOD 接口影响控制在最小范围内:先把服务端、网页端和售票机办卡流程落好,Lua 检票端则按“卡数据同步到服务端、设备侧尽量兼容现有事件格式”的思路实现。等你补到精确接口名时,只需要替换设备读写那一层。
先梳理 TicketMachine 现有 Lua 工程结构,定位办卡、支付、兑卡、闸机扣费和外设事件相关代码,再基于现有流程做兼容式扩展。
先查看 TicketMachine 项目的结构和现有网页/后端实现方式,确认应该在什么技术栈和目录下扩展 IC 卡相关功能。
已经定位到 server 和 web 目录,下一步会读现有数据层、路由和票务页面,沿用当前项目的接口风格与样式结构扩展 IC 卡功能。
已定位到核心文件主要集中在 ticketmachine.lua 和 gate.lua,接下来读取主流程并搜索现有卡务、支付、同步、外设事件入口。
主流程里目前已有纸票购票和线上券核销基础,但还没有会员卡/IC 卡独立状态与首充支付流程。我在继续查找是否已有服务端卡同步字段或外设读卡事件约定,避免破坏现有事件模型。
现有项目已经有票务、订单和公共查询的雏形,我在继续确认后台单页应用的导航与渲染方式,再把 IC 卡页面无缝接进去。
我在改动前先补齐后台入口与现有订票页的上下文;刚才直接读大文件超时了,改用检索关键片段定位页面和渲染函数。
还需要对齐服务端已有接口字段,尤其是 /tickets/check 和线上订单相关返回结构,这样 Lua 侧新增卡务字段才能尽量复用当前同步模型。
我已经确认后台是 Vue 单页,接下来直接抓取 index.js 里视图状态和票据模块的关键代码片段,避免盲改导航和渲染逻辑。
我已经确认仓库里还有 Node 服务端代码,接下来直接读服务端入口,核对 Lua 侧要提交的字段名和可复用接口。
当前结构已经够用,我准备开始落地实现:先补数据层和 API,再把后台列表详情与公开查询/购卡页接上现有导航和样式。
先获取后台模板和脚本的精确片段,随后会直接修改导航、数据源与新页面文件。
服务端文件较大,我改用按路由片段抓取,重点看售票上传、检票、订单查询/核销几段实现。
准备按两条线改:
ticketmachine.lua增加卡务状态、办卡入口、姓名软键盘、首充金额与支付确认、线上兑卡查询与确认,并把写卡/建卡动作做成“优先调用已存在外设能力,缺失时保留同步与日志”的兼容实现。gate.lua保持ticket_scanned事件不变,新增 IC 卡识别与服务端同步扣费分支,优先走卡专用同步接口,失败时不影响原纸票检票逻辑,同时把返回的余额/进站站点等字段尽量写回现有updateTicket。开始改ticketmachine.lua,先补通用请求与卡务辅助函数,再接页面流转和确认逻辑。
我在按最小侵入方案设计 IC 卡数据模型:一张卡对应基础信息、状态、余额和事件流;线上购卡会生成订单号并同步落卡,后台直接基于这套记录做列表和详情。
辅助函数已补进主文件,现在先回读最新片段,再接首页入口、卡务页面和确认提交流程,避免和刚才的改动冲突。
首页和卡务子页面已经接上,下一步改确认页与完成页,让票务和卡务共用支付界面,但按不同产品分支处理。
先改服务端数据层,增加 IC 卡主表与事件流,后面的管理和公开页面都会直接复用这套接口。
现在补管理 API:提供 IC 卡列表、详情、建卡、编辑和充值,前端后台页面就能直接接这几个接口。
再补公开 API:支持线上购卡下单、按卡号或订单号查询,以及给购卡页读取卡种配置。
后端数据和公开接口已经补上,接着把新页面路由挂到 Express,保证 IC 卡查询和购卡页可以直接访问。
售票机主线基本串起来了,开始处理 gate.lua:保持现有 ticket_scanned 事件入口不变,只在识别到 IC 卡字段时切到卡务同步分支。
我改成最小侵入的后台接入方式:在现有控制台侧边栏增加一个独立的 IC 卡管理页入口,这样不用重写原有 Vue 控制台也能保持一致风格。
接下来新增三个页面文件:后台 IC 卡管理页、公开查询页和线上购卡页;页面结构会直接复用现在的深色后台和 JR 风格公开页。
代码已经落到两个 Lua 主文件里,我先跑编辑器诊断看有没有明显语法或引用问题,再决定是否需要补一轮修正。
诊断没报语法问题,但我在回读 gate.lua 时发现一个运行时风险:getCardId() 提前引用了后面才声明的 firstNonEmpty(),这类 Lua 闭包在本文件写法下可能取到全局空值。我先修这个,再做一次最终检查。
语法检查已经过了,环境里没有可直接调用的 lua/luajit 解释器,所以验证会以编辑器诊断和静态流程核对为主。我再取一份变更摘要和关键片段行号,方便你对照 review。
再做一处稳健性修正:writeICCard() 目前把“调用成功但返回 false”也当成写卡成功,我把这个分支收紧,避免外设明确失败时误报完成。
页面文件已经生成,最后补一组很薄的样式扩展,让 IC 卡表单、卡种卡片和后台详情区能贴合现有视觉体系。
我顺手把首页入口补上,让新增的 IC 卡查询和线上购卡页能从门户页直接进入,不会变成隐藏功能。
已修改
- ticketmachine.lua:首页新增
CARD入口,加入办卡子菜单、姓名输入软键盘、首充金额选择、线上兑卡页。 - ticketmachine.lua:确认页兼容票务/卡务双模式,首充走现有右侧红石支付,线上兑卡支持免现场支付确认。
- ticketmachine.lua:新增卡片完成页与
logs/last_card_issue.json快照记录,保留原纸票完成页。 - ticketmachine.lua:补充卡片写卡辅助、卡务同步请求封装、外设方法兼容探测。
- gate.lua:新增 IC 卡识别字段解析,兼容
card_id、余额、媒体类型等扫描数据。 - gate.lua:新增 IC 卡进出站同步扣费分支,按服务端返回写回
entered/exited/balance/entry_station/exit_station/last_fare。 - gate.lua:主循环继续只监听
ticket_scanned,检测到卡数据时切到卡务分支,纸票分支保持原逻辑。
实现说明
- 办卡流程:
Home -> CARD -> OPEN CARD -> 姓名输入 -> 首充金额 -> 支付确认 -> 写卡完成。 - 姓名输入:使用屏幕软键盘与
char/key事件,支持字母、数字、空格,限制 12 字符。 - 首充支付:复用原
order页面与右侧红石投币/支付事件,尽量不改既有外设触发方式。 - 线上兑卡:
CARD -> ONLINE REDEEM输入 5 位码,读取/public/orders/:code,识别为卡订单后直接进入确认页。 - 写卡兼容:优先尝试
issueCard / issueICCard / writeCard / writeICCard / issueTicketData / writeTicketData,成功后记录快照;明确返回false时判定写卡失败。 - 闸机扣费:继续走服务端判定模型,只是把纸票
/tickets/check扩到卡片/cards/check,并保留双向闸机的自动进出站推断逻辑。
假设
- 服务端提供
POST /api/cards/open用于办卡/线上兑卡建卡,返回可包含card_id / balance / deposit / topup。 - 服务端提供
POST /api/cards/check,返回结构尽量仿照现有/api/tickets/check,至少包含result,可选reason / balance / fare / entry_station / exit_station。 - IC 卡介质仍通过现有
ticket_scanned事件上报,只是扫描表里会带card_id、balance、media等字段。 - 售票机外设若没有上述写卡方法,则本次实现会停在“写卡失败”而不会伪装成功;同时保留
logs/last_card_issue.json便于后续对接外设。 - 姓名输入按当前 CC:Tweaked 屏幕与键盘能力处理为 ASCII 软键盘,不做中文输入法。
验证结果
- 诊断检查:
ticketmachine.lua、gate.lua的 VS Code 诊断均为0错误。 - 运行环境检查:当前环境未发现
lua或luajit可执行文件,无法做本地解释执行验证。 - 静态流程核对:已逐段检查办卡首页流转、姓名输入、首充确认、线上兑卡确认、卡完成页,以及闸机
ticket_scanned -> isICCardScan() -> handleICCardScan()分支闭环。 - 仓库状态:当前目录不是 Git 仓库,无法直接给出
git diff/提交号;如果你需要,我可以下一步按这两份文件再整理一版人工 diff 摘要。
建议下一步
- 先在服务端补齐或确认
/api/cards/open、/api/cards/check的字段名。 - 在真实外设上确认
ticket_vending_machine的写卡方法名;如果你给我外设 API,我可以把兼容探测改成精确调用。 - 如果你希望,我可以继续补一版服务端
cards路由,和网页后台里的卡订单管理。
home.html 修改时工具超时了,我改用精确替换的方式更新首页卡片文案和入口,避免影响现有结构。
功能已经接上,下面开始做静态检查和实际启动验证,优先排语法错误和明显的页面脚本问题。
语法检查已通过。现在启动一次本地服务并打几个新接口,确认路由和页面文件都能被服务器正常挂载。
服务正在拉起,我改用更小输出重试状态检查,确认端口和启动日志即可。
新页面请求返回了 404,我在看服务端运行日志和实际加载的 server/index.js,确认是不是需要重启或有路由没生效。
端口 23333 已被现有进程占用,所以我改在 23334 启动修改后的版本做独立验证,这样能确认新增路由确实来自本次代码。
新页面路由已经在 23334 验证为 200。我再补一次接口验证,确认健康检查和新增 IC 卡公开配置接口也可响应。
再做一组端到端验证:调用线上购卡接口生成一张测试卡,再用公开查询接口回查,确认关键流程闭环可用。
我补一个容错修正:让 IC 卡事件流同时写入内存缓存。这样即使本地没起 MySQL,测试环境下也能正常看到详情时间线,不影响正式有库部署。
事件缓存修正已完成,我会重启 23334 的验证实例,再确认查询接口能带出事件记录。
已完成
- 已在项目中补齐 IC 卡数据存储、管理 API、公开 API、后台列表详情页、公开查询页、线上购卡页,并复用现有深色后台与 JR 风格公开页样式。
- 后端新增
ic_cards/ic_card_events数据结构,同时做了内存缓存兜底,便于本地无 MySQL 时继续联调。 - 后台入口已加到现有控制台侧栏,首页也补了 IC 卡相关服务入口。
修改文件
- 后端:
- 后台页面:
- 公开页面:
- 样式:
新增能力
- 管理 API:
GET /api/ic-cardsGET /api/ic-cards/:idPOST /api/ic-cardsPUT /api/ic-cards/:idPOST /api/ic-cards/:id/topup
- 公开 API:
GET /api/public/ic-cards/configGET /api/public/ic-cards/query?q=...GET /api/public/ic-cards/orders/:codePOST /api/public/ic-cards/orders
- 页面路由:
/admin/ic-card/ic-card/order/ic-card/search
验证结果
- 语法检查通过:
node --check server/index.jsnode --check server/routes/api.jsnode --check server/routes/public.jsnode --check server/services/data.jsnode --check web/ic-card-admin.jsnode --check web/ic-card-order.jsnode --check web/ic-card-search.js
- 编辑后诊断结果:
GetDiagnostics返回空,无新增诊断错误。
- 本地服务验证:
- 因现有
23333端口已被占用,验证实例使用PORT=23334 GET http://127.0.0.1:23334/ic-card/order返回200GET http://127.0.0.1:23334/ic-card/search返回200GET http://127.0.0.1:23334/admin/ic-card返回200GET http://127.0.0.1:23334/api/public/health返回正常GET http://127.0.0.1:23334/api/public/ic-cards/config返回卡种配置GET http://127.0.0.1:23334/api/ic-cards返回空列表后可正常写入POST /api/public/ic-cards/orders已成功生成测试订单与卡号GET /api/public/ic-cards/query?q=卡号/订单号已成功回查,并能返回事件流POST /api/ic-cards与GET /api/ic-cards/:id已成功验证后台建卡与详情读取
- 因现有
说明
- 本机
127.0.0.1:3306当前未启动 MySQL,启动日志里有ECONNREFUSED;因此本次运行验证主要依赖新增的内存缓存兜底。 - 正式环境只要 MySQL 可用,代码已会自动建表并写入
ic_cards、ic_card_events。
两块改动都已经落下来了。我先在主会话里做一次交叉核对,确认新增的 IC 路由、页面入口和 Lua 页面状态都在项目里,并补跑一次诊断,避免子任务改完后还有漏项。
我做交叉核对时发现一个需要马上确认的点:Lua 侧现在已经调用了 /api/cards/open 和 /api/cards/check,但我还要再检查服务端是否真的暴露了这两个设备接口,避免页面改完但机台联不起来。
我确认到一个真实问题了:目前后端有 ic-cards 的管理和公开页面接口,但还没有机台/闸机直接调用的 /api/cards/open、/api/cards/check 设备接口。
这会导致网页能用、Lua 机台却打不到后端。我现在补这一层,让售票机办卡和闸机扣费真正闭环。
我要补两个设备端关键点:一是线上兑卡查询改成优先走新的 IC 卡公开接口,避免机台还去查旧票据凭证;二是后端补出 /api/cards/open 和 /api/cards/check,把办卡、进站、出站扣费都接上。
先查看 api.js 和 ticketmachine.lua 的现状,确认现有卡务接口、扣费流程,以及线上兑卡当前调用链,再进行最小范围修改和验证。
已定位到服务端路由和 Lua 客户端文件,接下来读取相关实现,确认 /public/ic-cards/orders/:code 现有返回结构以及当前线上兑卡分支如何落到本地逻辑。
文件直读超时了,我先用内容检索切出关键片段,定位新增接口应插入的位置,以及 Lua 里当前兑卡查询顺序。
已经锁定到关键行号,但编辑前还需要把这些行附近的代码完整取出来。我改用终端分段读取,避免 IDE 读大文件超时。
关键片段已经拿到一部分。现在继续补齐 tickets/check、cards/open 调用方和 ticketmachine.lua 的兑卡分支,这样就能准确对齐请求/响应字段。
现在已经能看到服务器和终端两端的关键入口。下一步会对齐 gate.lua 期望的 /api/cards/check 响应格式,然后在 api.js 里补足对应卡片状态流转。
准备开始改文件前,我先确认工作区是否已有未提交改动,避免覆盖你现有编辑;然后会按最小补丁方式更新 api.js 和 ticketmachine.lua。
准备写入 api.js:会新增卡片下发与检票两个接口,并复用现有 DataService/LogicService,保证返回结构能直接被 ticketmachine.lua 和 gate.lua 消费。
接着修改 ticketmachine.lua 的兑卡查询顺序,只动线上兑卡入口,先查卡单接口,再回退旧票券接口,避免影响普通车票兑换流程。
代码已写入。现在回读关键片段确认没有插错位置,再做语法/诊断检查与最小验证。
语法诊断已经通过。为了补一层验证,我再做一次 JS 语法检查,并确认路由已按现有 server/index.js 的 /api 挂载方式生效。
关键改动和验证都完成了,我再补一遍精确位置,方便你直接点开对应代码。
修改结果
- 在 api.js 新增了
POST /cards/open与POST /cards/check两个接口,实际挂载路径是/api/cards/open与/api/cards/check,挂载位置见 index.js。 - 在 api.js 补了卡单号、金额、车站解析等辅助方法,供新卡务接口复用。
- 在 ticketmachine.lua 调整了线上兑卡逻辑,先查
/public/ic-cards/orders/:code,失败后再回退/public/orders/:code,并对非pending_pickup的卡单直接提示Already Used!。
接口行为
POST /api/cards/open- 支持本地办卡:生成或激活
card_id,落库为active,记录开卡事件。 - 支持线上兑卡:按
voucher_code/order_code查找pending_pickup卡单,兑卡成功后改为active,写入兑卡站点、设备、余额等信息。 - 已兑过的线上卡单会返回
409,Lua 端会显示Already Used!。
- 支持本地办卡:生成或激活
POST /api/cards/check- 支持 IC 卡进站:记录
entry_station、进站时间与当前卡状态。 - 支持 IC 卡出站:按
entry_station -> 当前站调用现有票价逻辑扣费,返回fare、balance/remaining_balance、entry_station、exit_station。 - 对
not_found、already_entered、not_entered、already_exited、insufficient_balance、fare_not_found等情况返回result: 'deny',与gate.lua当前处理方式兼容。
- 支持 IC 卡进站:记录
验证情况
api.js通过node --check语法检查。GetDiagnostics检查 api.js 与 ticketmachine.lua 均无诊断错误。- 已确认新接口会被现有
/api路由挂载,不需要额外改server/index.js。
补充说明
- 这次验证是静态语法与编辑器诊断级别,未做带真实卡数据/站点票价数据的联机跑单。
- 如果你要,我下一步可以继续帮你补一组最小联调用例,直接给出
curl或PowerShell Invoke-RestMethod的办卡、兑卡、进站、出站验证脚本。
已完成
- 已把 IC 储蓄卡主流程接进项目:售票机办卡、首次充值支付、检票机进出站扣费、网页后台记录、公开查询、线上购卡与机台兑卡。
- 售票机首页新增
CARD入口,支持“办卡”和“线上兑卡”,办卡流程含持有人输入、首充金额选择、支付确认、出卡完成页,主改动在 ticketmachine.lua、ticketmachine.lua、ticketmachine.lua、ticketmachine.lua。 - 线上兑卡已改为优先查询新的 IC 卡订单接口,位置在 ticketmachine.lua。
- 检票机已增加 IC 卡分支,支持根据进站站号和当前站号计算路费并扣减余额,再把结果写回卡数据,主改动在 gate.lua 和 gate.lua。
- 后端已补齐 IC 卡存储和设备接口,包括
/api/cards/open、/api/cards/check、后台/api/ic-cards*、公开/api/public/ic-cards*,主改动在 api.js、api.js、public.js、data.js、data.js。 - 网页端已新增后台卡管理页、公开购卡页、公开查卡页,并接入现有站点风格与路由,主改动在 index.html、home.html、ic-card-admin.html、ic-card-admin.js、ic-card-order.html、ic-card-order.js、ic-card-search.html、ic-card-search.js、style.css。
- 服务端页面路由也已补上,见 server/index.js。
关键说明
- 机台写卡这一层做了兼容探测,会按顺序尝试
issueCard、issueICCard、writeCard、writeICCard、issueTicketData、writeTicketData,实现位置在 ticketmachine.lua。 - 这是为了兼容你目前给出的 MOD 信息不完整的情况;也就是说,服务端与网页闭环已经补齐,但如果 MOD 最终实际写卡函数名不同,还需要把这层适配精确收口一次。
- 进出站状态提示仍沿用现有逻辑,
already_entered、not_entered、already_exited这类提示链路没有拆掉,只是新增了卡的余额与扣费分支。 - 卡数据会同步写入服务端,并记录事件流,后台与公开查询都能查到。
验证
- 我做了主会话交叉核对,确认新入口和新路由已经落在代码里,关键命中包括 ticketmachine.lua、api.js、api.js。
- 全局
GetDiagnostics返回空,没有看到新增诊断错误。 - 之前子任务里也完成了 Node 语法检查和页面/API 基本联通检查。
残余风险
- 最大风险仍然是 MOD 侧真实的“写卡/回写卡字段”函数签名未最终确认;现在代码已按兼容方式实现,但如果你那边的真实接口名、参数顺序、返回字段不同,需要再做一次精准对接。
- 目前售票机姓名输入是 ASCII 软键盘,不支持中文输入法;如果你要中文持有人名,需要 MOD 或 CC 侧另给输入方案。
- 如果你愿意,下一步我可以直接继续做两件事中的任意一件:
- 给你一份“MOD 侧最终应提供的精确接口清单”,你发给做 MOD 的人即可。
- 给你一组联调脚本,按“办卡 -> 兑卡 -> 进站 -> 出站”完整跑通。
User
现在有好多内容都出现了乱码!请修复
Assistant
我先定位哪些文件出现了编码乱码,重点检查刚改过的网页、Lua 和后端文件里的中文文本,再逐个修复成统一编码内容。
我先排除了 node_modules 里的无关命中。现在重点看项目自身文件,尤其是网页页头有没有缺少 UTF-8 声明,以及哪些中文文本已经被写坏。
已经确认至少 web/home.html 的中文文本本身被写坏了,不是浏览器编码声明问题;web/index.html 只有一处注释/注释尾部残缺。
我继续扫描公开页面和脚本里是否还有类似 鍙、绗、鏈 这类乱码串,然后统一修复。
我已经定位到主要乱码源就在 web/home.html,内容像是被错误编码后又保存了一次。我直接把这页的中文文案全部重写为正常 UTF-8,同时顺手修掉几个被截断的标签文本。
home.html 的主体乱码已经换回正常中文了。我再做一轮扫描,把剩余的残留乱码和注释损坏一并清掉,然后检查有没有因此引入语法问题。
现在只剩 web/index.html 里一处注释残留乱码,虽然不影响显示,但我把它也修掉,保持文件干净。
已修复
- 主要乱码来源确认是 home.html,这不是浏览器编码声明缺失,而是文件内容本身已经被错误编码后保存了。
- 我已经把这页的中文文案全部恢复为正常 UTF-8,包括标题、导航、首页说明、服务卡片、提醒文案、票价图/线路图提示和页脚备案号。
- 同时清理了 index.html 里残留的一处乱码注释。
修改文件
检查结果
- 再次扫描
web目录后,项目自身网页文件里已没有同类乱码残留。 - home.html 的诊断结果为空。
- index.html 的诊断结果也为空。
说明
- 这次确认到的乱码集中在首页 home.html,其他我本轮扫描到的网页、Lua、JS 文件没有发现同类坏字串。
- 如果你现在浏览器里还有页面显示乱码,通常是浏览器缓存或服务器还在提供旧文件;建议先强刷一次页面。
建议
- 先重点刷新首页、线上购卡页、IC 卡查询页确认显示是否正常。
- 如果你还看到别的页面有乱码,直接把页面路径发我,我可以继续逐页清。
User
还是有乱码字符 div
线上购卡的css失效了 https://ticket.fse-media.group/ic-card/order
Assistant
我先做两件事并行确认:一是继续扫描 home.html 是否还有漏掉的乱码文本,二是检查 ic-card/order 页面为什么样式没生效,重点看 HTML 引用、CSS 文件、以及线上实际页面加载情况。
我已经看到一个很像根因的问题:ic-card/order 页面里 CSS 用的是相对路径 style.css?v=6,在 /ic-card/order 这个地址下会被浏览器解析成 /ic-card/style.css?v=6,这很可能直接 404。
我再用浏览器和线上请求确认一下,然后一并修复相对路径和剩余乱码。
问题确认了两点:
线上购卡样式失效的根因就是 CSS 路径写成了相对路径,线上实际请求了https://ticket.fse-media.group/ic-card/style.css,而这个地址返回Cannot GET。home.html里还残留了一处乱码文本绾胯矾璧勬簮。
我现在直接修这两个点。
我又确认到一个更完整的原因:线上购卡 不只是 CSS,相对路径的 logo.png、ic-card-order.js、public-status.js 也都会在 /ic-card/order 下解析错。
所以这次我会把公开页面里所有静态资源统一改成站点根路径,避免同类问题反复出现。
我已经把公开页面的静态资源路径统一改成绝对路径了,这样 /ic-card/order、/ic-card/search 这类带子路径的页面就不会再把 CSS 和脚本请求到错误地址。
接下来我做一轮快速验证,确认没有残留相对路径和新语法问题。
已修
- 修掉了
home.html里残留的乱码文案,绾胯矾璧勬簮已恢复为“线路资源”,位置在 home.html。 - 修掉了
线上购卡样式失效的根因:/ic-card/order页面原来用的是相对路径style.css?v=6,浏览器实际请求成了https://ticket.fse-media.group/ic-card/style.css,线上返回了Cannot GET /ic-card/style.css。 - 现在已把公开页面静态资源统一改成根路径,避免带子路径的页面再次把资源请求错位置。
已改文件
修复内容
- 把
href="style.css?...改为href="/style.css?... - 把
src="logo.png"改为src="/logo.png" - 把
public-status.js、ic-card-order.js、ic-card-search.js、ticket-order.js、ticket-search.js、token.js改为根路径引用 - 这样
/ic-card/order和/ic-card/search不会再去请求/ic-card/style.css、/ic-card/ic-card-order.js这类错误地址
验证
- 我直接验证了线上请求:
https://ticket.fse-media.group/ic-card/style.css?v=6当前确实返回Cannot GET /ic-card/style.csshttps://ticket.fse-media.group/style.css?v=6可以正常返回 CSS- 修改后的几个 HTML 文件诊断都为空,没有新增语法错误
说明
- 你现在浏览器里还能看到那个乱码
div,大概率是页面还没重新加载到最新文件,或者线上站点还没重新部署/重载静态文件。 - 代码层面这两个问题都已经修好了;如果线上 URL 还是旧表现,需要让当前服务重新读到这些改动。
下一步
- 先强制刷新
home和https://ticket.fse-media.group/ic-card/order - 如果这是你本机运行的 Node 服务,重启一下站点进程
- 如果你愿意,我可以继续把其他仍使用相对静态资源路径的页面也一并扫完,例如
blog.html、ticket-route.html、login.html,一次性消掉同类隐患
User
购卡界面去掉卡类型,改成首次充值:5、10、15、20、自定义,去掉备注,去掉联系电话,然后持卡人名改成只能英文+符号,去掉押金
Assistant
我先改线上购卡这一套:页面表单、前端校验和后端下单字段一起收口,确保“卡类型/备注/联系电话/押金”都真正去掉,不只是前端隐藏。
我已经确认线上购卡页和公开下单接口目前都还带着“卡类型、押金、备注、联系电话”这套旧逻辑。现在把它们一起改成“首次充值”模式,并把姓名限制改成英文+符号校验。
我会顺手把公开查询页里和这次规则冲突的“卡种、押金、备注”展示也收一下,避免用户购卡后在查询页又看到旧概念。
我把界面结构改成“首次充值按钮 + 自定义金额”,并同步把前后端字段削到只剩 holder_name 和 initial_balance,这样规则不会前端后端不一致。
我已经把公开购卡页和后端下单接口都改成“首次充值”模型了。现在做一轮语法和残留字段检查,确认前端没有还在引用被删掉的 phone、note、card_type。
已修改
- 线上购卡页去掉了“卡类型”“备注”“联系电话”,改成“首次充值”模式,支持
5 / 10 / 15 / 20 / 自定义,改动在 ic-card-order.html 和 ic-card-order.js。 - 持卡人姓名现在限制为“英文 + 常用符号”,不允许中文和数字;前端和后端都做了校验,后端规则在 public.js。
- 押金已从线上购卡逻辑中移除,购卡金额现在等于首次充值金额;公开下单接口也不再使用
phone、note、card_type作为必需/核心字段,见 public.js 和 public.js。 - 公开查询页同步去掉了“押金”和“备注”展示,改为显示“首次充值”,见 ic-card-search.js。
现在的购卡规则
- 卡片固定按
IC 储值卡处理,不再让用户选卡种。 - 首次充值可直接点
5 / 10 / 15 / 20,也可切到“自定义”手输金额。 - 持卡人姓名必须匹配英文和常用符号格式,例如
Alex Smith、A.Brown、Chris-O'Neil。 - 押金固定为
0,实付金额就是首次充值金额。
验证
- ic-card-order.html、ic-card-order.js、ic-card-search.js、public.js 的诊断均为空。
node --check校验 public.js 通过。
提醒
- 线上站点要看到新表单,需要重载当前服务或重新部署静态文件。
- 如果你还想把后台
IC 卡管理里的“卡种 / 联系电话 / 押金 / 备注”也一起删掉,我可以下一步继续统一那一页。
User
继续统一
Assistant
我继续把后台 IC 卡管理 一并统一到新规则,重点清掉“卡种、联系电话、押金、备注”相关表单和展示,保留持卡人姓名、状态、余额这几个真正还在使用的字段。
我把后台这边也按同一套规则收口:建卡只保留“持卡人姓名 + 初始余额”,详情只保留“姓名 + 状态 + 余额”,其余旧字段全部从界面上撤掉。必要的话,我也会顺手把后台接口默认值改成和公开购卡一致。
后台页面和后台 API 已经按新规则收口了。现在做最后一轮残留字段扫描和语法检查,确认没有再引用旧的 联系电话 / 卡种 / 押金 / 备注 输入。
已统一
- 后台
IC 卡管理现在也按同一规则收口了,不再出现“卡种、联系电话、押金、备注”这套旧字段。 - 快速建卡只保留
持卡人姓名(仅英文与符号)和初始余额,改动在 ic-card-admin.html。 - 后台详情页只保留
持卡人 / 卡片类型(固定显示 IC 储值卡)/ 状态 / 余额,并把摘要区补成“首次充值 + 购卡金额”,改动在 ic-card-admin.js。 - 后台列表里的卡片副标题也统一成
持卡人 · IC 储值卡,不再根据旧card_type显示月票卡/纪念卡。
接口同步
- 后台 API 现在也统一默认按
IC 储值卡处理,建卡和更新都不再接受前端传入的phone / note / deposit / card_type作为主逻辑字段,改动在 api.js。 - 后台建卡和编辑同样加入了“持卡人姓名仅支持英文与常用符号”的校验,和公开购卡页保持一致。
- 后台卡片列表搜索也去掉了对
phone、note的依赖。
验证
- ic-card-admin.html、ic-card-admin.js、api.js 诊断均为空。
node --check校验 api.js 已通过。- 我也额外扫过后台页面代码,已没有
createPhone、createType、detailPhone、detailType、detailDeposit、detailNote这类旧字段残留。
当前统一结果
- 公开购卡页:只填持卡人姓名,选择首次充值金额。
- 公开查询页:不再展示押金、备注。
- 后台管理页:不再编辑卡种、联系电话、押金、备注。
- 后台和公开端:都使用同一套英文姓名校验规则。
如果继续
- 现在还可以再统一一层:把设备端
/api/cards/open里残留的phone / note / deposit / card_type兼容字段也进一步收窄成纯“姓名 + 余额”模型。 - 如果你要,我下一步就继续清这一层。
User
没有显示出充值配置 Uncaught TypeError: Cannot read properties of null (reading 'addEventListener') at ic-card-order.js:167:22 at ic-card-order.js:182:3
Assistant
我先对照 ic-card-order.html 和 ic-card-order.js 定位是哪一个元素 ID 没对上,再补一层空值保护,避免单个 DOM 缺失就直接把整页脚本打挂。
我确认这更像是“线上实际 HTML 还是旧版、JS 已经是新版”造成的前后不匹配:旧页面没有 #customInitialBalance,新版脚本一绑定事件就炸了。
我现在把 ic-card-order.js 做成向后兼容,能同时兼容新旧 DOM,避免线上缓存/灰度状态下直接白屏。
我已经把线上购卡脚本改成兼容新旧页面结构了:即使线上还在缓存旧 HTML,也不会因为找不到新 ID 直接抛错。再做一轮诊断确认没有手滑引入新问题。
已修
- 这个报错的根因是线上页面很可能还是旧版 HTML,而浏览器拿到了新版
ic-card-order.js。 - 新版脚本会找
#customInitialBalance和#rechargeOptionList,但旧版页面只有#initialBalance和#planList,所以在绑定addEventListener时直接对null调用了方法。
这次改动
- 我把 ic-card-order.js 改成了兼容新旧 DOM:
#rechargeOptionList不存在时,自动回退到旧的#planList#customInitialBalance不存在时,自动回退到旧的#initialBalance- 如果关键节点仍然缺失,会先
console.error并安全退出,不再整页脚本崩掉 - 旧页面里残留的
联系电话、备注输入框会被脚本自动禁用并隐藏,避免旧 HTML 和新逻辑混在一起
验证
- ic-card-order.js 诊断为空,没有新增错误。
你现在应该看到的效果
- 即使线上还是旧 HTML,页面也不该再报
Cannot read properties of null (reading 'addEventListener') - 充值配置应该能正常渲染出来
- 旧的“联系电话/备注”如果还在缓存页面里,也会被隐藏掉
说明
- 这属于典型的“HTML 和 JS 部署版本不一致”问题。
- 代码侧我已经做了兼容,但如果你想完全干净,还是建议把线上静态页面一起更新并清缓存。
建议
- 强刷
https://ticket.fse-media.group/ic-card/order - 如果服务端有静态缓存/CDN,也一起清掉
- 如果刷新后还有问题,我可以继续帮你直接排线上实际返回的 HTML 和 JS 是否版本不一致
User
现在似乎cc端没有正确传给售票方块出卡参数,导致没有正常出卡。
Assistant
我先对照售票机 Lua 里“购票正常出票”的调用方式,检查办卡分支到底传了哪些参数给 ticket_vending_machine,再和你给的 MOD API issueICCard() 对比,找出哪里少传或传错了。
我基本锁定根因了:当前 CC 端 writeICCard() 是按“带参数写卡”去试 issueICCard 的,但你最早给的 MOD API 里 issueICCard() 是无参。
这就很可能导致外设方法被调用时参数不匹配,最终根本没成功出卡。我先把这一层改成优先按真实无参 API 调用,再兼容旧的猜测式写法。
我发现这块不只是“参数不对”,还有更深一层的问题:如果 MOD 的 issueICCard() 真的是无参并返回真实卡号,那现在 CC 端是“先向服务端开卡,再尝试往方块写卡”,顺序也反了。
正确闭环应该更像“先让售票方块实际出卡拿到真实 cardId,再把这个 cardId 连同姓名/余额同步到服务端”。我先确认 firstString() 这类工具函数怎么取返回值,再按这个思路改。
我按“真实无参 issueICCard() 优先”的方式改:先让方块出卡拿到物理 cardId,再把这个 cardId 发给服务端建卡/兑卡。旧的“带参数写卡”逻辑保留作兼容后备。
我已经把办卡/兑卡的出卡顺序改成“优先真实无参 issueICCard() 出物理卡,再把返回的 cardId 同步给服务端”。同时保留了旧的参数写卡逻辑作为后备,避免兼容老实现时直接失效。
已修方向
- 我已经把 CC 端出卡流程改成更符合你给的 MOD API:
- 先调用售票方块的无参
issueICCard()尝试实际出卡 - 拿到它返回的真实
cardId - 再把这个
cardId + 持卡人 + 余额发给服务端/cards/open - 只有当无参出卡接口不存在或失败时,才回退到之前那套“服务端先建卡,再尝试参数写卡”的兼容逻辑
为什么之前会出问题
- 你给的 API 是
local ok, cardId = vm.issueICCard() - 但原来的
ticketmachine.lua把issueICCard当成“可带参数写卡”的方法来试,这很容易导致外设调用参数不匹配,最终不出卡 - 更严重的是,原来顺序是“先服务端生成卡号,再让方块出卡”,如果方块自己返回了一个真实
cardId,那物理卡号和服务端卡号还会错位
这次改动
- 新增了物理出卡 helper,在 ticketmachine.lua
- 调整了办卡确认分支,优先走“先出卡再同步”,位置在 ticketmachine.lua
- 旧的
writeICCard()仍保留为 fallback,并补了一些兼容字段和无参尝试,位置在 ticketmachine.lua
现在行为
- 如果
ticket_vending_machine.issueICCard()正常返回true, cardId- 会直接用这个
cardId去服务端开卡/兑卡 - 这样服务端和实体卡号一致
- 会直接用这个
- 如果物理卡已经出了,但服务端同步失败
- 页面会显示
Issued, sync failed: ... - 不会误报“办卡成功”
- 页面会显示
- 如果方块出了卡但没返回
cardId- 页面会显示
Card issued without ID - 方便你继续判断 MOD 侧返回值是否异常
- 页面会显示
验证
- ticketmachine.lua 诊断为空,没有新增语法问题。
建议你现在测试
- 在 CC 端实际点一次办卡
- 看售票机屏幕提示是否从之前的写卡失败,变成正常
Card ready - 如果还不行,优先看两件事:
issueICCard()是否真的返回了true, cardId- 物理卡出了以后,服务端
/cards/open是否返回成功
如果继续排
- 我下一步可以直接给你加一版更明确的现场调试输出:
- 在 CC 屏幕或
logs/last_card_issue.json里把issueICCard()的返回值、方法名、最终提交给/cards/open的cardId都打印出来
这样就能马上判断是“方块没出卡”,还是“出了卡但没回传卡号”,还是“服务端同步失败”。
User
还是方块没出卡,你先确定这里的代码没有问题
Assistant
我按代码审查方式只看这条链路:ticketmachine.lua 里办卡确认 → 调用售票方块 → 同步 /cards/open。先把相关代码和服务端接口完整读一遍,再给你结论,优先找“必然导致不出卡/错卡”的问题。
我还要核对一件关键事:正常出票 issueTicket(...) 是怎么调用的,以及 ticketVendingMachine 这个外设对象到底是怎么拿到的。这样才能判断现在 issueICCard() 的调用方式是不是和现有外设包装层一致。
我已经抓到至少一个会直接影响“没出卡却被当成成功分支处理”的硬问题,现在做第二遍交叉验证,避免把猜测当结论给你。
先定位 ticketmachine.lua 和 server/routes/api.js 中办卡、兑卡与 /cards/open 的实现,再沿着调用链核对 card_id 的来源、落库逻辑,以及同步失败后的重试/去重控制。
先查看 ticketmachine.lua 里这两个函数及其调用链,核实返回值约定和失败回退路径,再给出逐条审查结论。
已经定位到关键判断:两处都用 r1 ~= false 作为成功条件,我继续查卡片开户流程里失败后的补偿/回退逻辑,确认是否会出现“没出卡但流程已成功/已扣款”的后果。
回退链路也查过了:当前卡开户分支只有 submitCardOpen 正向调用,没有对应撤销/退款/作废分支;接下来我按“是否会把 nil 当成功、是否会导致不出卡不回退”分别给出结论和等级。
我已经看到终端机分成两条路径:一条先出空白卡再调 /cards/open,另一条先调接口再写卡。接下来补看服务端 upsert 与订单索引逻辑,确认线上兑卡是否真的保留设备上报的 card_id,以及“同步失败后重试”会不会再放一张新卡。
我在补读辅助函数时遇到一次读取超时,改成更小范围继续取证。重点会放在 normalizeIcCardId、findIcCardByOrderCode、upsertIcCard,这些能直接决定是否覆盖 card_id 和是否允许重复兑卡。
补充看了一下服务端路由,/cards/open 存在,但暂时没看到与这条开卡调用成对的回滚接口;我再取一小段路由确认一下,避免把“前端无回退”误说成“系统绝对无回退”。
发现
严重:writeICCard的成功判断会把nil误判为成功;一旦外设方法“正常返回但首返回值是 nil”,代码仍会走成功分支,直接把订单标记为confirmed = true并显示Card ready,即使实际没有写卡/出卡。ticketmachine.lua:L362-L364 ticketmachine.lua:L1611-L1617结论:这里的nil返回会被误判为成功,且会导致“可能没出卡却不回退”。更严重的是,submitCardOpen(payload)在写卡前已把服务端卡状态创建/激活,当前ticketmachine.lua中没有对应撤销/退款/回退逻辑。ticketmachine.lua:L1594-L1617 api.js:L729-L818高:issueBlankICCard的成功判断同样会把nil误判为成功;但它的后果和writeICCard不完全一样。这里不会直接把 UI 标成成功,而是进入elseif okIssueBlank分支,报Card issued without ID,同时跳过后续submitCardOpen + writeICCard的后备路径。ticketmachine.lua:L378-L380 ticketmachine.lua:L1568-L1592结论:issueBlankICCard的nil返回也会被误判为成功;如果这个nil实际表示“未出空白卡/未拿到有效结果”,当前流程不会回退付款,也不会进入后备写卡路径,结果是订单停在失败态但没有补偿逻辑,属于“支付后可能不出卡且不回退”。ticketmachine.lua:L1568-L1594
流程图
flowchart TD
A[确认开卡] --> B[issueBlankICCard]
B -->|pcall成功 且 r1=nil| C[被判定为 okIssueBlank=true]
C --> D{blankCardId 是否为空}
D -->|为空| E[显示 Card issued without ID]
E --> F[不走 submitCardOpen/writeICCard]
F --> G[无回退/退款逻辑]
B -->|返回 false| H[submitCardOpen]
H --> I[writeICCard]
I -->|pcall成功 且 r1=nil| J[被判定为 okWrite=true]
J --> K[confirmed=true + Card ready]
K --> L[即使未实际写卡/出卡也结束交易]
style C fill:#fff3e0,color:#e65100
style E fill:#fff3e0,color:#e65100
style J fill:#ffcdd2,color:#b71c1c
style K fill:#ffcdd2,color:#b71c1c
style G fill:#ffcdd2,color:#b71c1c
style L fill:#ffcdd2,color:#b71c1c
审查表
| No. | Issue Title | Suggestion | Code Link |
|---|---|---|---|
| 1 | 严重 writeICCard 经由 callPeripheralMethods 将 nil 当成功,可能未写卡却显示成功 |
成功条件改为“显式成功值”或“显式拿到有效卡标识/写卡结果”,不要用 r1 ~= false;同时在 submitCardOpen 成功但写卡失败时增加补偿逻辑 |
ticketmachine.lua:L362-L364, ticketmachine.lua:L1611-L1617 |
| 2 | 高 issueBlankICCard 将 nil 当成功,导致跳过后备路径并卡死已支付交易 |
issueBlankICCard 仅在返回明确成功信号时才置 okIssueBlank=true;若无卡号,应继续尝试后备路径或进入补偿/退款流程,而不是停在“issued without ID” |
ticketmachine.lua:L378-L380, ticketmachine.lua:L1568-L1592 |
补充说明
nil会被误判为成功,这一点是代码语义上可以直接确认的,因为 Lua 中nil ~= false为真。- 两处问题的区别在于:
issueBlankICCard的误判更像“内部成功态误判,阻断后续分支”;writeICCard的误判则是“最终用户可见成功误判”,风险更高。 - 就当前代码范围看,确实存在“可能不出卡却不回退”的结果;其中
writeICCard路径是最明确、最严重的一条。
如果你要,我可以下一步直接把这两处成功判断改成“只接受显式成功返回”,并补一个最小化的失败补偿方案。
关键证据已经比较清楚了:线上卡订单在下单时就预生成了 card_id,而终端兑卡时又会先从设备拿真实空白卡号再上报。如果 /cards/open 不接纳这个真实卡号,就会出现“数据库里是预生成号,实体卡上是设备号”的错位;我再补一眼 /cards/check 的查卡逻辑,确认这个错位会不会直接导致刷卡失败。
结论
flowchart TD
A[线上下单生成待领卡订单] --> B[public.js 预生成 card_id]
B --> C[终端兑卡先从设备拿空白卡真实 card_id]
C --> D[/cards/open 兑卡分支]
D --> E[沿用订单原 card_id]
E --> F[返回旧 card_id 给终端]
F --> G[数据库 card_id 与实体卡真实 card_id 错位]
H[终端确认办卡/兑卡] --> I[先 issueBlankICCard 出卡]
I --> J[再调用 /cards/open 同步]
J --> K{同步失败?}
K -->|是| L[仅提示 Issued, sync failed]
L --> M[processing=false 且 confirmed=false]
M --> N[CONFIRM 再次可点]
N --> I
style B fill:#bbdefb,color:#0d47a1
style E fill:#fff3e0,color:#e65100
style G fill:#ffcdd2,color:#b71c1c
style I fill:#f3e5f5,color:#7b1fa2
style L fill:#fff3e0,color:#e65100
style N fill:#ffcdd2,color:#b71c1c
-
1. 严重线上兑卡会忽略设备返回的card_id。 结论:会。在线订单在创建时就预生成了一个card_id,public.js;终端兑卡时先从设备拿到真实空白卡号并放进payload.card_id,ticketmachine.lua。但/cards/open的兑卡分支只用findIcCardByOrderCode()找到原订单后直接upsert {...current, ...},没有把body.card_id写回卡记录,因此仍保留订单原来的card_id,api.js。随后终端又优先采用服务端返回的respData.card_id覆盖本地显示值,ticketmachine.lua。而后续验卡是按上报的card_id直接查索引,api.js,所以一旦设备真实卡号与订单预生成号不一致,就会形成“实体卡号和后台卡号错位”。 -
2. 严重同步失败后允许重复出卡。 结论:允许。无论办新卡还是兑卡,终端在确认时都会先执行issueBlankICCard()出卡,再调用/cards/open同步,ticketmachine.lua。如果同步失败,代码只是提示Issued, sync failed,没有落本地待补偿队列,也没有把本次出卡锁死;随后processing被清回false,confirmed仍是false,CONFIRM按钮重新可点,ticketmachine.lua。同时当前工程的离线补偿队列只覆盖车票上传,不覆盖卡开卡/兑卡,ticketmachine.lua 和 ticketmachine.lua。这意味着操作员重试时会再次先出一张新卡;若第一次其实已被服务端受理但响应丢失,第二次重试在兑卡场景会先出第二张卡再收到409 already redeemed,在普通开卡场景还可能直接生成第二张有效卡。
补充
- 未看到能够推翻上述两点的保护逻辑。
- 这两个问题都不是纯日志/展示问题,都会影响线上实体卡与后台状态的一致性。
- 如需,我可以下一步直接给出修复方案,或按“先修
card_id错位 / 先修重复出卡”分别落代码。
我已经确认:这段代码目前还不能说没问题,而且有两处是会直接影响实体卡流程的硬问题。
我先把问题按严重级别列清楚,再由你选是一起修还是先修最影响出卡的那一处。
你这边已经选了“全部修复”,我就直接动手。
这次我会同时修 3 件事:nil 误判成功、线上兑卡忽略真实 card_id、同步失败后重复点确认会再次出卡。
我要顺手确认一下 DataService.upsertIcCard() 在“在线兑卡时替换 card_id”这个场景下是否会清理旧索引;如果不会,我会一起补上,避免后台里残留旧卡号脏数据。
我现在开始改 Lua 主流程,重点是让“重试只重试同步、不再重复出卡”,并把外设成功判断收严成“必须显式成功”。
Lua 侧第一轮已改完。接着补服务端 /cards/open 的兑卡分支,把设备真实 card_id 接住并替换旧预生成卡号,避免实体卡和后台错位。
我补一处刚才顺手写错的服务端语法:把误写成 Lua 的 ~= 改回 JS 比较,并顺便把旧卡号变量提出来,避免重复归一化。
结论
- 这段代码原来不是没问题,我确认到了 3 个会影响实体卡流程的硬问题,并且已经全部修掉。
- 现在这条链路从静态代码上看已经顺了:外设成功判断更严格、线上兑卡会接住真实
card_id、同步失败后不会再次重复出卡。 - 语法和诊断已通过:
ticketmachine.lua与api.js都无诊断错误,node --check也通过。
本次修复
nil误判成功已修:外设调用现在只接受“显式成功返回”,不再把nil当成功,位置在 ticketmachine.lua。- 同步失败重复出卡已修:如果空白卡已经出过但服务端同步失败,后续重试会复用同一张已出的卡号,只重试同步,不会再调用方块重新出卡,位置在 ticketmachine.lua。
- 线上兑卡卡号错位已修:
/cards/open的兑卡分支现在会采用设备返回的真实card_id,并清理旧的预生成卡号记录,位置在 api.js。
流程图
flowchart TD
A[CC 确认办卡/兑卡] --> B{已有待同步空白卡号?}
B -->|否| C[调用 issueICCard 出实体卡]
B -->|是| D[复用上次 blank card_id]
C --> E{返回显式成功且有 card_id}
E -->|是| F[/cards/open 同步服务端]
E -->|否| G[停在失败态,不误判成功]
D --> F
F -->|成功| H[Card ready]
F -->|失败| I[保留 pendingBlankCardId,仅重试同步]
style C fill:#bbdefb,color:#0d47a1
style F fill:#c8e6c9,color:#1a5e20
style G fill:#ffcdd2,color:#b71c1c
style I fill:#fff3e0,color:#e65100
我确认过的关键点
- 成功判断:
r1 ~= false已改成“r1不能是nil/false”,避免外设没回值时被当成成功。 - 重试逻辑:同步失败后会记住
pendingBlankCardId,下一次CONFIRM不会再出第二张卡。 - 在线兑卡:服务端不再死守下单时预生成的卡号,而是跟随设备真实卡号。
现在如果还不出卡,优先怀疑代码外因素
ticket_vending_machine外设没有被peripheral.find('ticket_vending_machine')找到。- MOD 实际方法名/返回值和你给的文档不一致,比如
issueICCard()并不返回true, cardId。 - 售票方块出卡前还有额外前置条件,例如库存、朝向、内部槽位、红石状态或实体卡物品未就绪。
下一步最有效
- 我建议直接再加一轮运行时日志,把这 4 个值打印出来:
ticketVendingMachine是否存在issueICCard是否存在pcall(issueICCard)的原始返回值- 最终提交到
/cards/open的card_id - 这样就能马上把问题切成“代码流程问题”还是“MOD/外设行为问题”。
状态
- 这轮代码我已经改完。
- 如果你愿意,我下一步直接给你把 CC 端调试日志也补上,专门抓“为什么方块没出卡”。
User
IC卡网页控制台端可以正常收到信息,也能看到卡号、持卡人。但是订单号那里显示“---”。而且页面上还是有联系方式,请删除。然后加一个“删除卡”的按钮。然后把这个IC卡管理的页合并到/admin页
Assistant
我先检查后台页面结构和 IC 卡管理页现状,重点看 3 件事:订单号为什么显示 ---、联系方式残留在哪里、以及现在 /admin 和独立 IC 卡页是怎么分开的。
我已经确认独立的 IC 卡页就是现在那套后台界面。接下来我会把它并到现有主控制台页,同时补上删除接口和删除按钮,再顺手查订单号为什么总是空。
我在看主控制台的 Vue 结构,判断是直接并一块原生区域,还是把现有 IC 卡逻辑迁进 Vue。优先会选改动更小但不别扭的方案。
我准备直接把 IC 卡管理迁进主控制台的 Vue 页,不走 iframe。这样 /admin 里能原生切标签、共享现有刷新逻辑,也方便加“删除卡”按钮和统一样式。
我开始改主控制台模板,把独立的 IC 卡区块直接并进去,同时会让旧地址 /admin/ic-card 自动落到这个新视图,不再维持两套后台入口。
主控制台里的 IC 卡视图已经嵌进去了。现在我先重新读一遍刚改过的 index.js,再补最后几处导出和路由改动,避免在同一文件上踩掉刚才的上下文。
我再补两层后端收口:去掉 IC 卡记录里的联系方式字段,并加删除卡接口。顺手把旧 /admin/ic-card 入口指向新的 /admin 视图。
我已经把主界面、后台接口和旧入口路由都串起来了。现在做最后一轮检查,确认 index.html/index.js/api.js/server/index.js 没有新报错,也确认后台页里不再残留“联系方式”。
最后我再抓一下关键改动的定位行,方便你直接点开看:主控制台入口、IC 卡视图、删除接口和旧路由跳转。
已调整
- IC 卡管理已并入主控制台
/admin,侧边栏不再跳独立页,而是在主控制台内切换到IC 卡管理视图,改动在 index.html 和 index.html。 - 订单号显示已改成优先读取
order_code / voucher_code / code,如果本来就是现场办卡没有线上订单,则显示“现场办卡”,不再一律是---,逻辑在 index.js。 - 后台页中的“联系方式”已去掉;我也确认新的
/admin页面代码里已经没有联系方式 / 联系电话 / phone残留。 - 卡片详情区新增了“删除卡”按钮,前端逻辑在 index.html 和 index.js。
接口变更
- 新增后台删除接口
DELETE /api/ic-cards/:id,会删除卡记录并记一条删除事件,位置在 api.js。 cards/open这条卡开卡接口里不再继续写phone,位置在 api.js。
路由合并
- 旧入口
/admin/ic-card现在会跳转到/admin?view=iccards,位置在 server/index.js。 - 旧兼容入口
/ic-card-admin也同样跳转到新的主控制台视图,位置在 server/index.js。 - 主控制台脚本会自动识别
view=iccards并直接打开 IC 卡管理页,位置在 index.js。
验证
- index.html、index.js、api.js、server/index.js 诊断均为空。
node --check已通过 index.js 和 api.js。- 我额外扫过合并后的后台页面代码,没再找到
联系方式 / 联系电话 / phone。
说明
- 独立文件
ic-card-admin.html/js还保留在仓库里,但运行时入口已经并回/admin了,用户访问旧地址会自动跳到新视图。 - 如果你愿意,我下一步可以继续把这两个旧文件也彻底清理掉,避免以后误维护两套页面。
User
在出卡的时候持卡人name是否正确传给mod
Assistant
我直接核对办卡这条调用链里“持卡人姓名”从页面状态到 ticket_vending_machine 外设调用的传递路径,重点确认有没有真正作为参数传到 MOD。
结论
- 按你现在给的 MOD API 来看,持卡人
name在主流程里没有直接传给 MOD 出卡方法。 - 当前代码的“优先路径”是先调无参
issueICCard()出空白卡,这一步不带姓名参数,位置在 ticketmachine.lua 和 ticketmachine.lua。 holder_name目前是先组进本地payload,然后发给你的网站后端/cards/open,位置在 ticketmachine.lua 和 ticketmachine.lua。
现在的实际行为
- 姓名来源:
state.holderName会写进payload.holder_name,见 ticketmachine.lua。 - 主出卡调用:
issueBlankICCard()只做pcall(fn),没有任何参数,见 ticketmachine.lua。 - 服务端同步:姓名会跟着
payload提交给/cards/open,所以网页控制台能看到持卡人姓名,这和你观察到的一致。
只有哪种情况会传给 MOD
- 只有走到后备路径
writeICCard(finalCard)时,代码才会尝试把姓名传给外设。 - 这里会把
holder_name填进payload.owner、payload.card_holder,并尝试多种参数形式调用外设,见 ticketmachine.lua。 - 例如它会尝试:
{ payload }
{ tostring(payload.holder_name or ''), tonumber(payload.initial_balance) or 0 }
{ tostring(payload.card_id or ''), tostring(payload.holder_name or ''), ... }
- 但这只是兼容性后备,不是当前你这套
issueICCard()API 的主流程。
所以答案是
- 如果 MOD 真实接口就是你发的:
local ok, cardId = vm.issueICCard()
- 那么答案是:没有,姓名没有在“出卡给 MOD”这一步传进去。
- 目前姓名只进了后端记录,没有确定写进实体 IC 卡的数据区。
建议
- 如果你希望“出卡时就把持卡人名写进卡里”,MOD 侧还需要提供明确的写卡接口,比如:
issueICCard(holderName, balance)- 或
issueICCard()后再writeCardData(cardId, data) - 如果你要,我下一步可以直接把 CC 端改成“只支持你最终确认的 MOD 写卡接口”,把这层彻底收口,不再靠兼容猜测。
User
调用是: vm.issueICCard("")
Assistant
我按你刚确认的真实调用签名把 CC 端收口到 vm.issueICCard("<name>"),这样主出卡路径会把持卡人姓名直接传给 MOD,不再走无参猜测。
我已经把主流程改成按姓名参数出卡了,同时把后备写卡路径也补上了“单参数姓名”这一种调用形式,避免 issueICCard(name) 被别的参数组合打偏。现在做一轮语法检查。
已改
- 现在主出卡调用已经按你给的真实签名改成:
vm.issueICCard("<name>")。 - 也就是说,持卡人姓名现在会在“出卡给 MOD”这一步直接传入,不再是之前的无参调用。
具体改动
issueBlankICCard()改成接收holderName,位置在 ticketmachine.lua。- 主办卡确认分支现在会把
payload.holder_name传给它,位置在 ticketmachine.lua。 - 后备写卡路径也补上了单参数调用形式
{ holder_name },这样如果走到兼容分支,也会优先尝试issueICCard(name),位置在 ticketmachine.lua。
现在的实际行为
- 办卡页输入的姓名先进入
payload.holder_name - 然后主流程调用:
ticketVendingMachine.issueICCard(payload.holder_name)
- 如果这种调用失败,代码才会退回到旧的兼容尝试
验证
- ticketmachine.lua 诊断为空,没有新增错误。
结论
- 现在可以明确说:持卡人 name 已经会正确传给 MOD 出卡方法。
- 如果接下来还是“不出卡”或“卡里名字不对”,问题就更可能在 MOD 侧实现,而不是这里没把参数传过去。
如果你要继续排
- 我下一步可以再把这次调用的运行时参数直接写到
logs/last_card_issue.json或屏幕调试输出里,确认现场实际传出去的名字字符串到底是什么。
User
办卡充值时不要计算手续费、押金等内容。就直接是充值多少付多少钱。
然后网页查询那里支持查询凭证码
Assistant
我先收口两处逻辑:CC 办卡支付金额只按首充金额计算,以及网页查询把“凭证码”也作为 IC 卡查询入口。随后会把前后端一起对齐。
我看到查询页目前只认 card_id / order_code,还没把线上购卡返回的“凭证码/兑换码”纳入匹配。我再把购卡下单返回字段和服务端存储字段核对一下,一并修成可按凭证码查。
我已经定位到 CC 端办卡金额仍然沿用了旧的 deposit + topup 计算。我现在把这套金额模型直接改成“首充即实付”,并同步把公开查询文案和后端匹配都加上“凭证码”。
金额逻辑和查询入口都已经改完。我现在做检查,确认 Lua 和网页脚本没有新报错,同时抓一下关键位置方便你直接点开看。
已修改
- 办卡充值现在不再计算手续费、押金等附加项,实付金额就是首充金额,改动在 ticketmachine.lua、ticketmachine.lua、ticketmachine.lua。
- CC 办卡确认页也去掉了押金显示,只保留
Top-up / Balance / Order,并且Order现在等于首充金额,见 ticketmachine.lua 和 ticketmachine.lua。 - 网页 IC 卡查询现在支持按
卡号 / 订单号 / 凭证码查询,后端匹配已补上,见 public.js。 - 查询页文案、输入提示和结果展示也已同步加入“凭证码”,见 ic-card-search.html 和 ic-card-search.js。
现在的行为
- 现场办卡:付
5就充5,付10就充10,不再额外加押金。 - 线上购卡查询:如果你拿到的是 IC 卡订单生成的凭证码,也可以直接在查询页输入。
- 查询结果里会显示“凭证码”字段;当前若没有单独存
voucher_code,会回退显示已有的order_code。
验证
- ticketmachine.lua、public.js、ic-card-search.html、ic-card-search.js 诊断均为空。
node --check已通过 public.js。
说明
- 你现在如果在线上页面还看不到新查询提示,通常是静态文件缓存;强刷
IC 卡查询页即可。 - 如果你还想更彻底一点,我下一步可以把后台
/admin里的 IC 卡搜索框也一起扩成支持“凭证码”检索。
User
把后台 /admin 里的 IC 卡搜索框也一起扩成支持“凭证码”检索。
Assistant
我先把 /admin 的 IC 卡搜索链路看一遍,确认是前端占位文案要改,还是后台 /api/ic-cards?q= 本身还没把“凭证码”纳入检索。
后台接口这边确实还没把 voucher_code/code 纳入过滤;前端占位文案也还是旧的。我直接一起补上。
我再做一轮检查,确认 /api/ic-cards?q= 和 /admin 的 IC 卡页都没有新问题。
已支持
/admin里的 IC 卡搜索框现在也支持按“凭证码”检索。- 后台接口
/api/ic-cards?q=已把voucher_code和code纳入搜索字段,改动在 api.js。 - 前端输入框提示文案也同步改成“搜索卡号 / 订单号 / 凭证码 / 姓名”,位置在 index.html。
验证
- api.js 和 index.html 诊断均为空。
node --check已通过 api.js。
说明
- 现在后台 IC 卡列表可按
card_id / order_code / voucher_code / code / holder_name / source命中。 - 如果线上数据里“凭证码”实际仍是写在
order_code,这次改动也兼容,不会影响原有搜索。
建议
- 强刷一下
/admin?view=iccards后测试一张线上购卡记录的凭证码。 - 如果你愿意,我还可以继续把后台 IC 卡列表里那行“订单 xxx”文案也改成“订单/凭证 xxx”,避免展示上看起来像只能搜订单号。
User
售票机办卡的时候也要传入余额参数: issueICCard(ownerName, initialBalance) — 第二参数可选,默认 0。 vm.issueICCard("张三", 50) -- 开卡,余额 50
Assistant
我把售票机办卡主调用再收口一次,改成按你最新确认的签名传 name + initialBalance,并同步检查后备分支的参数顺序。
我顺手扫到办卡金额选择页里还有一处旧公式残留,仍在用 押金 + 充值。这次会一起修掉,避免界面金额和实际出卡参数不一致。
我开始改 ticketmachine.lua:主出卡改成 issueICCard(name, initialBalance),并把办卡金额页里残留的押金公式和提示一起清干净。
主调用已经改完了。我现在做一轮诊断,确认 Lua 没有语法问题,并把关键位置抓出来给你确认。
已修改
- 售票机办卡主调用现在会把“持卡人姓名 + 初始余额”一起传给 MOD:
vm.issueICCard(ownerName, initialBalance)
- 对应改动在 ticketmachine.lua 和 ticketmachine.lua。
现在的实际调用
- 主流程会调用:
issueBlankICCard(payload.holder_name, payload.balance)
- 而
issueBlankICCard()内部优先尝试:
ticketVendingMachine.issueICCard(holderName, initialBalance)
- 位置分别在 ticketmachine.lua 和 ticketmachine.lua。
兼容处理
- 如果
issueICCard(name, balance)失败,代码还会回退尝试:issueICCard(name)issueICCard()
- 这样既满足你现在确认的真实签名,也不会把旧兼容路径直接砍掉。
顺手修正
- 办卡充值页里残留的旧公式也一起修了,不再出现
押金 + 充值的计算。 - 现在
Need Pay直接等于首充金额,位置在 ticketmachine.lua。
验证
- ticketmachine.lua 诊断为空,没有新增错误。
结论
- 现在 CC 端已经会按你给的最新接口,把姓名和余额一起传给 MOD 出卡。
- 如果接下来实体卡余额仍不对,就更像是 MOD 侧没有正确使用第二参数,而不是 CC 端没传。
User
线上购卡这里没有正确出五位凭证码,出了一个ICO-xxxx,请修改。 然后一些订单信息的字颜色要醒目一些。
我希望卡片信息也有一个单独的界面,类似电子客票。 在ticket.fse-media.group/ic/<卡号>
cc的屏幕键盘要加上符号的按钮
Assistant
我先核对 4 条链路:线上购卡凭证码生成规则、购卡结果页样式、独立卡片详情页路由,以及 CC 办卡软键盘的按键布局。
我看到一个关键不一致:CC 线上兑卡当前就只接受 5 位码,但网页购卡后端还在生成 ICO-xxxxxx。我会把后端和结果页一起改成统一的 5 位凭证码,并顺手把乱码 HTML 一并清掉。
我再看一下现有的 5 位凭证码生成器和“电子客票”样式,尽量复用现成的设计语言,不新造一套。
我已经确认这次有三类改动要一起做:凭证码统一为 5 位、独立 IC 卡详情页、CC 符号键盘;同时会把购卡结果卡片的关键信息做成更醒目的高对比展示。
我准备开始改后端与页面文件了:先统一生成 5 位凭证码,再补独立卡片详情页和更醒目的结果展示,最后处理 CC 键盘。
我先把后端码制改掉:线上购卡返回的主标识改成 5 位凭证码,并把卡数据里也显式保留 voucher_code/code 字段,方便展示和查询。
我已经开始做页面层改造了。接下来把新卡片详情页路由接上,并新建 /ic/<卡号> 页面文件。
CC 这边我会把姓名输入键盘做成 ABC / SYM 切换,不只补符号按钮,也顺手把输入约束收口到“英文 + 常用符号 + 空格”。
我在补最后的细节检查前,先把新详情页脚本做一层转义处理,避免事件详情里有特殊字符时把 DOM 撑坏。
我开始做最后一轮检查:后端码制、两个网页脚本、新增详情页和 CC Lua 一起过一遍,避免这波改动互相打架。
诊断已经空了。我再做一层语法校验,并抓关键定位行,方便你直接跳到 5 位凭证码、独立卡片页和符号键盘这几处确认。
我再抓一下几处精确行号:5 位凭证码、独立卡片页入口、购卡结果链接,以及 CC ABC/SYM 键盘按钮,方便你直接点进去看。
新页面的行号我再单独补一下,上一轮通配路径没命中。
已修改
- 线上购卡凭证码已改成统一
5位,不再生成ICO-xxxx;生成逻辑在 public.js,新建卡记录时也会同时写入voucher_code/code字段,位置在 public.js。 - 购卡结果区已改成更醒目的高对比展示,重点强化了
凭证码 / 卡号 / 首次充值 / 状态的颜色和层级,主逻辑在 ic-card-order.js,样式在 style.css。 ic-card-order.html这页我也顺手清掉了残留乱码,并把文案统一成“凭证码”口径,文件在 ic-card-order.html。
新页面
- 已新增独立 IC 卡详情页入口
ticket.fse-media.group/ic/<卡号>,服务端路由在 server/index.js。 - 新页面模板在 ic-card-detail.html,走的是类似电子客票的双栏卡片样式。
- 页面数据通过公开查询接口加载,脚本在 ic-card-detail.js。
- 线上购卡成功后,结果卡片里已新增“卡片详情”按钮,会直接跳到
/ic/<卡号>,位置在 ic-card-order.js。
CC 键盘
- 办卡姓名输入已新增
ABC / SYM切换按钮,支持常用符号键盘。 - 现在姓名输入会走“英文 + 常用符号 + 空格”的收口逻辑,相关代码在 ticketmachine.lua。
SYM按钮和字符追加逻辑分别在 ticketmachine.lua 和 ticketmachine.lua。
验证
- public.js、server/index.js、ic-card-order.html、ic-card-order.js、ic-card-detail.html、ic-card-detail.js、style.css、ticketmachine.lua 诊断都为空。
node --check已通过 public.js、server/index.js、ic-card-order.js、ic-card-detail.js。
注意
- 这次改的是“新生成的线上购卡凭证码”规则;历史上已经生成出来的
ICO-xxxx老记录不会自动改写,但现有查询仍兼容旧值。 - 线上要看到新页面和 5 位凭证码,需要重新部署或重载当前服务,并强刷
ic-card/order。 - 如果你要,我下一步可以继续把
IC 卡查询页也加一个“查看电子卡片页”按钮,直接从查询结果跳到/ic/<卡号>。
User
线上购卡那里 自定义充值的框放到那个按钮下面,就是把按钮的高度换成现在的一半,然后把输入框放到下面。 有个问题:首次充值的卡(未使用),但是卡详情里面显示余额10,首次充值30. 我们直接改成只显示余额就可以了不显示首次充值
Assistant
我先改线上购卡页的自定义充值布局,再把 IC 卡详情页里“首次充值”字段去掉,只保留余额,避免和实际卡内余额混淆。
我把自定义金额输入框从“持卡人信息”区挪到充值选项区下面,并且会把“自定义”按钮做得更紧凑一些。然后把详情页里的“首次充值”整块删掉。
我已经把布局和详情字段都改完了。现在做一轮检查,确认购卡页和详情页没有新增错误。
我再抓一下关键位置的行号:自定义充值框的新位置、按钮高度样式,以及详情页去掉“首次充值”后的字段区。
已调整
- 线上购卡页的“自定义充值”输入框已经移到充值按钮区下面,位置在 ic-card-order.html。
- “自定义”按钮高度已改成更紧凑的一版,并在选中时显示下面的输入框,逻辑在 ic-card-order.js。
- 对应样式在 style.css。
卡详情页
/ic/<卡号>详情页里已经去掉“首次充值”字段,只保留当前余额 / 凭证码 / 开卡时间,模板在 ic-card-detail.html。- 详情页脚本也同步去掉了对“首次充值”的赋值,现在只写余额、凭证码和开卡时间,见 ic-card-detail.js。
现在效果
- 充值区:先点
自定义,下面才会出现金额输入框。 - 输入区:持卡人姓名表单不再混着自定义金额输入框。
- 卡详情页:不会再出现“余额 10、首次充值 30”这种并排冲突展示,只显示当前余额。
验证
提示
- 线上页面如果暂时还是旧布局,强刷
ic-card/order和/ic/<卡号>即可。 - 如果你愿意,我还可以顺手把
IC 卡查询页里的“首次充值”字段也一起去掉,整站统一成只显示余额。
User
没有看到“自定义充值”输入框
Assistant
我先核对购卡页当前 DOM 和切换逻辑,确认是“按钮没切到显示状态”,还是输入框实际被隐藏在别处。
我确认到问题点了:现在这版是“只有点了自定义才显示输入框”,在缓存或旧静态文件混用时很容易看不到。
我准备改成更稳妥的方案:输入框固定显示在按钮区下面,点“自定义”时启用并聚焦,选预设金额时禁用但仍可见。
我已经把输入框改成固定显示了,现在只是在“自定义”时高亮并可输入。再做一轮检查确认没有新错误。
已修
- 我把“自定义充值”输入框改成了固定显示在按钮区下面,不再依赖“点了才出现”的隐藏逻辑。
- 现在的行为是:
- 选
5 / 10 / 15 / 20时:输入框可见,但禁用 - 选
自定义时:输入框可见、启用,并自动聚焦
- 选
改动位置
- 输入框固定放在充值按钮区下方:ic-card-order.html
- 切换逻辑改成“高亮/启用”,不再做隐藏:ic-card-order.js
- 样式补成固定可见的盒子:style.css
验证
你现在应该看到
- “自定义”按钮下方始终有一个输入框
- 点击“自定义”后,这个输入框会变为可输入状态
提醒
- 如果线上还没看到,先强刷
ic-card/order,因为这次改的是静态页面和前端脚本。
User
卡ID要统一啊,现在各个界面显示的都不一样,有00020,有64AE5246
Assistant
我先把 IC 卡 card_id 的生成和展示链路梳理一下,重点找出哪里在用“预生成卡号”,哪里在用“MOD 实际返回卡号”,再统一成一套口径。
我已经定位到根因了:
现在系统里同时存在两种 ID 口径。
- 线上购卡下单时,后端先预生成一个占位卡号,比如
IC-000020 - 真正出卡时,MOD 又返回实体卡号,比如
64AE5246
所以有些页面拿的是“预生成号”,有些页面拿的是“实体卡号”,就分裂了。
我再确认一下检票和查卡接口现在是按哪一个字段找卡,避免直接改显示把进出站链路弄断。
我已经确认到一个关键点:后台和公开页面很多地方都直接渲染 card.card_id,而这个字段在“线上待领卡”和“实体卡已出卡”两个阶段含义不一样。
我再读一下 /api/cards/check,确认它是不是强依赖当前这条主键,避免我统一显示后把检票查卡弄断。
我准备把“卡 ID”的显示口径收成一条规则:
只有 MOD/实体卡真正拿到的那个 ID 才当作对外卡号;线上待领卡阶段不再把 IC-000020 这种内部占位号当成“卡 ID”展示。
具体会做两层:
- 后端统一给卡对象补一个
display_card_id - 前端各页面都改成显示这个字段,不再直接拿
card_id上屏
这样至少页面上不会再一会儿 00020、一会儿 64AE5246。
我开始改后端响应和前端展示,把“显示用卡号”抽成一个统一字段,不碰底层实际查卡主键,这样不会影响检票和数据同步。
后端已经补好了统一展示字段。现在把前端几个页面都改成只显示 display_card_id,不再直接把内部 card_id 上屏。
我顺手把旧的独立 IC 管理页也一并改一下,避免你本地如果还开着旧文件时继续看到两套卡号。
我已经把主后台、公开查询、详情页、购卡结果页和旧后台页的显示都收成统一字段了。现在做一轮检查,确保没有新增报错。
我再把关键定位行抓一下,方便你直接点进去核对“显示用卡号”的规则和几个页面的替换点。
先继续收口“卡 ID 统一显示”这件事,我会先把代码里所有仍然直接显示 card_id 的地方找出来,再核对后端返回的展示字段是否已经统一。
User
IC卡详情页东西不要那么多,就和电子客票上显示的内容差不多就好了。
Assistant
我继续把卡号显示统一收口,同时会顺手把 IC 卡详情页精简成接近电子客票的展示,不再堆太多字段。
现在看到后端已经在返回 display_card_id,但详情页和结果页还保留了不少“真实卡号”直出逻辑;我先把这些展示入口统一掉,再把详情页内容裁成电子客票那种简洁版。
我准备直接改两个点:一是把 ic-card-detail 页面收成单卡片、少字段的电子客票样式;二是把待领卡状态下的跳转和复制逻辑改成优先用凭证码,避免看到占位卡号。
我已经把详情页主体裁掉了“最近记录”等重内容,接下来再扫一遍剩余直接露出原始 card_id 的前端代码,避免还有漏网的显示入口。
细节页和线上购卡页已经没有新的报错,我再把旧的 ic-card-admin 残留提示也顺手统一一下,避免弹窗里还冒出原始占位号。
已调整
- 已把详情页精简成接近电子客票的单卡片样式,只保留
卡号 / 持卡人 / 余额 / 凭证码 / 开卡时间 / 状态提示 / 操作按钮,去掉了“最近记录”等过重内容,见 ic-card-detail.html - 已把详情页逻辑改成统一优先显示
display_card_id,并根据状态自动切换提示文案;待领卡时按钮会优先按“凭证码”处理,避免又露出占位卡号,见 ic-card-detail.js - 已把线上购卡结果页的“查询此卡 / 卡片详情”跳转改成待领卡时优先使用凭证码,避免后续实体卡出卡后链接还绑着旧占位号,见 ic-card-order.js
- 旧后台页里的创建成功提示和充值弹窗也改成走统一展示口径,不再直接弹原始
card_id,见 ic-card-admin.js
当前效果
- 详情页现在会更像电子客票,不再堆很多字段。
- 待领卡状态下,页面展示继续是统一口径,不会一会儿是
IC-000020一会儿又是别的显示。 - 已启用实体卡时,仍然显示并复制真实卡号,检票主键逻辑没有动。
检查
- 已检查
ic-card-detail.html、ic-card-detail.js、ic-card-order.js、ic-card-admin.js的诊断,当前无新增报错。
建议
- 如果你要再进一步统一,我下一步可以把公开查询页
ic-card-search也继续裁成和电子客票同一套信息密度,这样所有对外页面观感会更一致。
User
去掉购卡的“订单号”吧。
然后在兑换卡的时候没有支付就直接出卡了。
卡号改成后面几位随机的。
控制台无法进入“IC卡管理”页
section 这一块直接拉宽到和上面 section 对齐
Assistant
我先分五块排查:购卡页文案、兑卡支付校验、卡号生成规则、后台 /admin 视图切换、以及详情页宽度布局,然后一起改掉。
我已经定位到后台入口和详情页布局点了,接着看卡号生成和 CC 兑卡支付流程,重点查是不是线上兑卡分支直接跳过了付费确认。
问题点找到了:ticketmachine.lua 的线上兑卡分支把 state.cost = 0 且 payment_mode = 'online',所以确认页会自动放行并直接出卡,这里我会改回“到机支付后才能兑卡”。
后台进不去的根因也找到了:顶部导航还写着旧地址 /ic-card-admin.html,而现在真正可用的是 /admin?view=iccards。我会一并改掉。
我会顺手把服务端也加一道“兑卡必须已支付”的校验,不只修前端/CC 流程,这样就算有人直接调接口也不能白拿卡。
我顺手把公开页文案里的“订单号”一起淡出,保留“凭证码”作为统一说法;后台内部检索能力不动。
改动已经落下去了,我先做一轮诊断检查,重点看 api/public 路由、后台页和购卡详情页有没有因为这些改动引入新错误。
已修改
- 去掉公开购卡/查询页里对“订单号”的展示和文案,统一对外强调“凭证码”,见 ic-card-search.js、ic-card-search.html、ic-card-order.html、ic-card-order.js
- 修了“兑卡不付钱直接出卡”问题:CC 端线上兑卡现在会按订单金额收款,且上送真实已付金额;服务端也新增了未付款拒绝兑卡的校验,见 ticketmachine.lua、ticketmachine.lua、api.js
- 把系统生成的 IC 卡号从顺序号改成
IC-后 6 位随机数,避免再是IC-000020这种递增格式,见 api.js、public.js - 修了后台“IC 卡管理”入口:顶部导航原来还指向旧地址
/ic-card-admin.html,现在统一跳到/admin?view=iccards,见 index.html、ic-card-admin.html、ticket-route.html - 把 IC 卡详情页那块
section拉宽,和上面的介绍区保持同一列宽,不再单独缩成 760,见 ic-card-detail.html
检查
- 已检查
server/routes/api.js、server/routes/public.js、web/index.html、web/ic-card-order.js、web/ic-card-search.js、web/ic-card-search.html、web/ic-card-detail.html、web/ticket-route.html,当前无新增诊断错误。 - 这次没有在实际 ComputerCraft 机器上跑兑卡流程,所以设备侧建议你再现场点一次“线上兑卡”确认红石付费后才会出卡。
建议验证
- 线上购卡新建一单,确认生成的占位卡号已变成随机 6 位。
- 在 CC 上输入凭证码后,不投币时应不能确认出卡。
- 后台顶部导航点击
IC 卡管理,应直接进入/admin?view=iccards。 /ic/<id>详情页中卡片主区域宽度应与上方介绍区对齐。