消费前一卡通单人信息校验设计
目标
消费模式第一次完整读卡成功后,不再使用读卡器返回的卡状态判断消费资格。终端必须先按读卡账号向一卡通服务器查询单人信息,以服务端返回的卡号、卡状态、卡序号和姓名形成权威人员快照;只有服务端状态为 1 时,零元核验或正金额扣款才能继续。
范围
- 覆盖零元订单和正金额订单的首次完整读卡流程。
- 调用一卡通
GetInfoByCustID,查询参数为首次读卡结果中的 CustomerID。
- 用服务端数据更新交易使用的卡号、卡状态、卡序号和持卡人姓名。
- 持卡人姓名进入消费页面、消费流程日志和本地交易 JSON 快照。
- 查询失败支持用户确认后重试;任何失败或非正常状态都不得发送扣款指令。
不修改业务订单查询协议、一卡通扣款算法和既有服务端上报字段契约;没有明确协议字段时,不向现有上报接口擅自增加姓名。
方案选择
采用“交易执行器前置校验门禁”方案:在读卡器串行交易流程中,把服务端单人查询放在首次完整读卡与零元分支/扣款分支之间,并通过独立网关和解析器隔离网络及字段规则。
未采用 Activity 普通异步回调方案,因为页面回调与扣款线程分离后容易出现查询尚未结束而交易继续的竞态;未采用本地人员快照替代方案,因为本地快照可能关闭、过期或尚未同步,不能满足消费前实时校验要求。
组件职责
单人信息模型与解析
新增只表达服务端权威人员字段的模型,包含账号、卡号、卡序号、状态和姓名。解析器从 SOAP 响应读取 CUSTOMERID、CARDNO、CARDSN、STATUS、NAME。
- 返回账号必须与请求账号一致。
- 五个字段都必须存在且格式合法;姓名去除首尾空白后不能为空。
- 卡号和卡序号必须在现有交易模型可表达的数值范围内。
- 状态只要求是合法整数;未知值属于“状态异常”,而不是“同步失败”。
服务端查询网关
查询网关冻结本次交易使用的一卡通服务器地址和 AppID,直接发起 GetInfoByCustID,不额外调用 GetCustomerCount。网络异常、SOAP Fault、查无人员、账号不一致和字段解析失败统一转换为“人员卡信息同步失败”。
卡片权威信息合并
UID、账号、钱包余额、消费计数和原始响应继续来源于读卡器;卡号、卡序号、卡状态和姓名由服务端覆盖,原始读卡值保留为诊断字段。正金额订单第二次读卡提供最新余额和计数,并在 UID、账号未变化后继承服务端身份字段。
状态规则
1:正常,可继续消费。
3:挂失,不可消费。
4:冻结,不可消费。
- 其他值:异常,不可消费。
消费资格只使用服务端状态。异常状态发生在扣款前,因此保留当前订单;移卡后允许换卡重试。
查询失败与人工重试
- 结束本次扣款前尝试,不执行成功反馈、扣款、消费后读卡或一卡通流水上传。
- 保存失败结果和错误分类,并展示“同步人员卡信息失败”确认弹框。
- 确认后对当前订单和当前卡片重新执行首次完整读卡及单人查询;成功后才继续交易。
- 取消后关闭弹框,保留订单并等待移卡;移卡后可重新放卡处理。
- 不执行无限自动重试。
页面、日志与数据库
- 消费页姓名直接使用本次服务端查询结果,不再依赖可选的本地人员快照。
- 首次读卡成功日志增加姓名、权威卡号、权威卡序号、权威状态和查询结果;原始字段使用
reader 前缀。
- 查询失败日志记录订单号、账号、失败类型、耗时和脱敏错误,不记录完整 SOAP 报文。
- 姓名及诊断字段通过现有前后交易 JSON 快照写入数据库,不新增重复列。
- 异常状态记录服务端状态值及分类。
并发与生命周期
- 单人查询与卡交易共用串行执行队列,保证读卡、查询、判断、扣款严格有序。
- 弹框只触发新的安全重试,不阻塞工作线程。
- 页面销毁、订单取消或卡片移除后,旧结果不得推进失效交易。
- 重试前由交易门禁确认订单状态,防止重复扣款。
测试
- 覆盖单人信息正常解析、缺字段、非法数字、账号不匹配、空姓名和未知状态。
- 覆盖状态
1、3、4 和其他值。
- 验证查询成功且正常才进入零元成功或扣款,失败及异常状态均不扣款。
- 验证确认重试、取消移卡、并发与失效订单门禁。
- 验证页面、日志、数据库前后快照中的姓名及权威字段。
- 回归全量同步、读卡、零元订单、补贴优先扣款、落库和双目标上传。
验收标准
- 服务端查询完成前没有任何扣款指令。
- 服务端状态只有
1 能继续,其他状态均在扣款前拦截。
- 查询失败弹出“同步人员卡信息失败”;确认可重试,取消后移卡可重新处理。
- 卡号、卡序号、状态和姓名来自服务端单人信息。
- 页面、流程日志和本地快照记录服务端姓名,前后快照身份一致。
- 失败、异常状态、重复确认和页面退出不会导致重复扣款。
- 单元测试和 Debug 构建通过;现场补充真实 SOAP 与真机扣款验收。