HarmonyOS ArkData Preferences:碰一碰落地页重入凭证幂等【鸿蒙心迹】
同一张优惠券入口被连续碰两次业务系统最怕的不是页面打开两遍而是把同一个领取动作执行两遍。第二次进入的页面看似正常后台却可能多了一条申请记录。对 HarmonyOS 上计划接入精准碰一碰的服务来说真正需要提前准备的是入口事件抵达业务层之后如何定义一次、如何记住成功、失败时又允许怎样重试。本文只构建碰一碰落地页的业务模拟器TapReceipt。TAP-071是演示任务 IDACK-071是示例回执它们不是系统提供的 NFC 参数格式。这里不展示未经核实的碰一碰系统回调不宣称已经连通设备间精确触发。未来接入真实系统入口时只需要把经过平台校验的业务参数交给相同的处理入口。一、先看重复到达后最危险的分叉落地页的一次事件至少经历“收到凭证、校验输入、查已有回执、执行业务、写入回执、展示结果”六步。如果第二次事件卡在第三步前到来单纯查一次本地 Key-Value 不能保证两次都不会执行。若第一次业务已经完成、但写回执失败下一次打开还可能误以为从未完成。因此业务目标不能写成泛泛的“防重复点击”。本篇定义两个层次在同一个进程里running集合阻止相同ticket同时进入在页面重新建立后Preferences 中的成功回执可以用于展示“已处理”。真正跨设备、跨进程或涉及资产发放的唯一性要由服务端以业务凭证建立幂等约束本地缓存不能替代。演示按这组数据组织第一次TAP-071获得ACK-071第二次同凭证到达时命中已存回执界面显示“重复已拦截”触发次数2、业务执行1、本地存储“已写入”。这只是预期状态轨迹相关图片是绘制演示不是手机现场测试结果。二、存储层只负责回答一个朴素的问题HarmonyOS ArkData 的 Preferences 适合保存轻量 Key-Value。官方参考说明getPreferences用于取得实例get读取put更新缓存XML 存储模式下再调用flush持久化。文档还特别提示它不保证多进程并发安全所以这里把存储能力限定在单进程 UI 演示绝不把它包装成全局事务锁。先将“查回执”和“写回执”放进独立的ReceiptStore.ets。这样 UI 只需要调用两个含义明确的方法不知道底层实例名和 Key 的组织方式。下面代码处理的是进程内轻量成功记录不存设备身份、位置、联系方式也不把业务秘密写进普通首选项。// entry/src/main/ets/storage/ReceiptStore.ets import { preferences } from kit.ArkData; import { common } from kit.AbilityKit; export class ReceiptStore { private db?: preferences.Preferences; async open(context: common.UIAbilityContext): Promisevoid { this.db await preferences.getPreferences(context, { name: tap_receipt_demo }); } async findAck(ticket: string): Promisestring { if (!this.db) { throw new Error(store not opened); } return String(await this.db.get(receipt_${ticket}, )); } async saveAck(ticket: string, ack: string): Promisevoid { if (!this.db) { throw new Error(store not opened); } await this.db.put(receipt_${ticket}, ack); await this.db.flush(); console.info(TapReceipt persisted ${ack}); } }这里按最普通的 XML 路径演示调用flush()如果工程明确切换到官方文档所述 GSKV 存储模式持久化方式应按对应规则调整而不是机械复制。findAck的默认值是空字符串意味着尚未找到成功回执并不代表上一次业务必然失败。初始open失败时应显示“存储不可用”不能带着未初始化的实例继续执行业务。三、成功回执必须在业务完成之后记录一个常见顺序错误是先写“已处理”再向服务端提交。这样服务端失败后客户端仍会把下一次重试挡掉。反过来服务端成功而本地落盘失败又存在重试风险。两种情况都不能靠 UI 的勾选状态修好服务端需要根据同一ticket返回同一结果。下面把业务调用注入ReceiptProcessor。为了不虚构网络 SDK示例使用一个确定性的commit函数模拟业务处理换成实际请求时必须保留业务凭证、鉴权和请求超时处理。running集合负责本进程内的并发门闩finally无论成功失败都会释放避免页面一直停在“处理中”。// entry/src/main/ets/model/ReceiptProcessor.ets import { ReceiptStore } from ../storage/ReceiptStore; export interface ReceiptResult { kind: string; ack: string; } export class ReceiptProcessor { private running: Setstring new Setstring(); private store: ReceiptStore; constructor(store: ReceiptStore) { this.store store; } async handle(ticket: string, commit: (id: string) Promisestring): PromiseReceiptResult { if (!/^TAP-\d{3}$/.test(ticket)) { throw new Error(invalid ticket); } if (this.running.has(ticket)) { return { kind: busy, ack: }; } this.running.add(ticket); try { const existed await this.store.findAck(ticket); if (existed.length 0) { console.info(TapReceipt duplicatetrue ack${existed}); return { kind: duplicate, ack: existed }; } const ack await commit(ticket); await this.store.saveAck(ticket, ack); return { kind: created, ack: ack }; } finally { this.running.delete(ticket); } } }注意这不是系统级 NFC 去重 APISet也不会跨进程保留。它只管理本次应用进程中正在处理的 ID。如果提交接口可能“服务器已执行、客户端超时”commit抛错时应保留“待核实”状态先查询服务端结果再决定是否重试不能无条件重新领取。四、落地页只消费已经归一化的凭证TapLanding.ets的按钮叫“再次触发”不是“模拟 NFC 成功”。开发者可以借助它重复注入同一个TAP-071稳定观察状态变化。未来从系统入口、二维码入口或业务链接进入时可复用onArrive(ticket)的后半段但前面的参数校验、签名和来源判断仍必须按实际平台能力实现。为了减少重复触发时的视觉抖动只有处理完成后才更新ack和状态。页面销毁以后尚未结束的异步调用可能仍有结果返回因此增加active门闩避免回写已经退出的界面它不是网络取消器真正耗时请求还需要独立的生命周期管理。// entry/src/main/ets/pages/TapLanding.ets import { common } from kit.AbilityKit; import { ReceiptStore } from ../storage/ReceiptStore; import { ReceiptProcessor } from ../model/ReceiptProcessor; Entry Component struct TapLanding { State ticket: string TAP-071; State ack: string ; State attempts: number 0; State stateText: string 待处理; private active: boolean false; private store: ReceiptStore new ReceiptStore(); private processor: ReceiptProcessor new ReceiptProcessor(this.store); aboutToAppear(): void { this.active true; this.bootstrap(); } aboutToDisappear(): void { this.active false; } private async bootstrap(): Promisevoid { try { await this.store.open(getContext(this) as common.UIAbilityContext); await this.onArrive(this.ticket); } catch (e) { if (this.active) { this.stateText 存储不可用; } } } private async onArrive(ticket: string): Promisevoid { this.attempts 1; console.info(TapReceipt arrive ${ticket} attempt${this.attempts}); try { const result await this.processor.handle(ticket, async (id: string) { // 仅演示真实场景由服务端返回受信任的回执。 return ACK-${id.split(-)[1]}; }); if (!this.active) { return; } if (result.kind busy) { this.stateText 处理中; return; } this.ack result.ack; this.stateText result.kind duplicate ? 重复已拦截 : 回执已保存; } catch (e) { if (this.active) { this.stateText 待核实; } } } build() { Column({ space: 18 }) { Text(碰一碰回执).fontSize(26) Text(任务 ID ${this.ticket}) Text(this.stateText) Text(回执 ID ${this.ack || —}) Text(触发次数 ${this.attempts}) Button(再次触发).onClick(() this.onArrive(this.ticket)) Text(演示数据 · 非真机碰一碰测试) }.padding(24) } }这里代码的aboutToAppear只是启动异步初始化不把事件认定成真实系统回调。真实入口可能晚到、重入、携带无效票据或被用户拒绝页面应允许展示拒绝原因而不应默认所有进来的数据都可调用提交动作。示例里的回执字符串只是演示规则任何真实业务都应由服务端产生或校验不可以直接按字符串拼接发放权益。快速重入得到busy时只显示“处理中”不把空回执覆盖到已完成的结果上。五、运行视图只能证明设计意图不能证明接口已接通预期轨迹为首次调用onArrive(TAP-071)后写入ACK-071界面出现“回执已保存”再次点击“再次触发”findAck找到相同回执显示“重复已拦截”。对应的演示日志分别是TapReceipt arrive TAP-071 attempt1、TapReceipt persisted ACK-071、TapReceipt arrive TAP-071 attempt2与TapReceipt duplicatetrue ackACK-071。检查这条轨迹时不能因为前台显示“业务执行 1 次”就说已验证支付或优惠券发放 exactly-once。那是样例展示值。真正需要记录的是ticket、服务端请求 ID、回执状态、提交耗时范围和错误类型但日志里不要打印鉴权令牌、用户手机号或可识别身份信息。更不能把演示的10:24状态栏时间误写成业务成功时间。还要专门测试三种中断本地写入回执前杀进程、flush返回错误、服务端成功而客户端未收到回执。第一种要走服务端查询第二种要保留可见的“待核实”第三种必须依赖同一业务键的服务端幂等。这里没有虚构测试通过率或真实设备表现以上是上线前应补齐的验收项。六、把接入边界留给真实系统能力精准碰一碰解决的是场景入口与近场触达本篇代码只负责触达之后的业务重复处理。碰一碰能力的开放条件、设备限制、接入配置和参数格式应以对应官方接入文档及当前开发者资格为准本文没有发明onTapNfc()一类不存在的接口。Preferences 存的是轻量状态适合恢复 UI 的“上次已经拿到什么回执”。它不是可信交易流水也不保证跨多进程原子性。业务交付时应将完整流程拆成“入口验证、客户端单进程门闩、服务端幂等、状态回查、异常告知”五层每层负责自己的失败模式。最重要的工程取舍是把重复抵达视为正常输入而不是偶发异常。页面可以被反复打开触发可以重复发生但有价值的业务动作必须基于受信任凭证确定唯一性本地回执只是让用户不必为同一个结果反复等待。参考文档HarmonyOS Preferences APIhttps://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-preferencesHarmonyOS 官方精准碰一碰讨论入口https://developer.huawei.com/consumer/cn/forum/topic/0208223729721325967鸿蒙生态解决方案白皮书近场业务入口https://developer.huawei.com/consumer/cn/doc/guidebook/solution-0000002755171974全部图片和日志均为独立生成的静态演示素材不属于真实系统碰一碰联调记录。

相关新闻

Java抽象类与接口:从设计思想到实战选型全解析

Java抽象类与接口:从设计思想到实战选型全解析

"day08 抽象类和接口"这个标题,我在带学生和做技术分享的时候见过太多次了。基本上每次讲到这个节点,课堂里就会出现一种"好像懂了但又说不清"的迷茫感。如果你也正处于这个阶段,别慌——这个知识点之所以绕,…

2026/10/11 23:23:23 阅读更多 →
5G分流比优化全攻略:从覆盖根因到智能调参实战

5G分流比优化全攻略:从覆盖根因到智能调参实战

简介:一份面向5G网络优化工程师及运营商技术人员的实战案例文档,聚焦巴中电信通过常规优化与创新试点提升5G分流比的全过程。内容覆盖低流量站点分析、高倒流区域识别、功率余量核查、4/5G互操作参数规整等常规手段,并引入波束下倾角调整与基…

2026/10/11 23:23:23 阅读更多 →
通用答辩PPT模板制作指南:版式规则、母版与避坑实战

通用答辩PPT模板制作指南:版式规则、母版与避坑实战

简介:这份PPT模板专为上海大学学生设计,覆盖毕业答辩、学术汇报、开题答辩与周会汇报等常见演示场景。模板内置清晰目录页、标题页、多样式内容页与图表展示模块,支持通过母版视图统一调整各级文本样式,也可在设计选项卡中添加形状…

2026/10/11 23:23:23 阅读更多 →

最新新闻

基于LSTM的五类动作识别源码包:从视频特征提取到可视化全流程

基于LSTM的五类动作识别源码包:从视频特征提取到可视化全流程

简介:这份资源面向深度学习入门者、课程设计与毕业设计学生,以及需要快速验证行为识别方案的技术人员,提供一套可直接运行的五类动作识别项目源码。项目覆盖jump、walk、pickup、salute、wave五种日常动作,并实现LSTM、TA-LSTM、S…

2026/10/13 1:59:18 阅读更多 →
Ventoy 多合一启动盘实战:一次部署,镜像即拷即用

Ventoy 多合一启动盘实战:一次部署,镜像即拷即用

简介:Ventoy 是一款开源的可启动 U 盘制作工具,面向系统运维、装机测试与多系统启动盘需求的技术人员。它解决的核心痛点是无需反复格式化磁盘,只需将 ISO/WIM/IMG/VHD(x)/EFI 等镜像文件复制到 U 盘即可启动,并支持一次放入多个镜…

2026/10/13 1:59:18 阅读更多 →
Win10下用VS2017与CMake编译OpenCV 4.5.1并集成contrib模块的完整指南

Win10下用VS2017与CMake编译OpenCV 4.5.1并集成contrib模块的完整指南

简介:一份专为Windows平台计算机视觉开发者准备的OpenCV 4.5.1contrib重新编译成果,基于win10、VS2017与CMake构建,解决在官方发行版中无法直接使用SIFT、SURF等contrib模块功能的痛点。包体为RAR压缩包,共992个文件、约47.65MB&a…

2026/10/13 1:59:18 阅读更多 →
STM32CubeIDE集成RT-Thread实战:从裸机到多任务调度

STM32CubeIDE集成RT-Thread实战:从裸机到多任务调度

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:59:18 阅读更多 →
Visual Studio 2010 Shell(Isolate):轻量级遗产系统调试环境搭建指南

Visual Studio 2010 Shell(Isolate):轻量级遗产系统调试环境搭建指南

简介:Microsoft Visual Studio 2010 Shell (Isolate) 是面向Windows平台C、C#、VB.NET等开发者的一套轻量级、可定制IDE运行环境,专为无需完整VS安装但需调试器、编辑器及扩展框架的场景设计,适用于旧系统维护、第三方工具集成或精简开发环境…

2026/10/13 1:59:18 阅读更多 →
JavaScript公式编辑器实战:从LaTeX渲染到存储回显全解析

JavaScript公式编辑器实战:从LaTeX渲染到存储回显全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:58:18 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →