【听见课堂 HarmonyOS NEXT 实战系列 09】本机优先不是一句口号:课堂无障碍应用的隐私数据流设计
【听见课堂 HarmonyOS NEXT 实战系列 09】本机优先不是一句口号课堂无障碍应用的隐私数据流设计课堂无障碍应用会接触麦克风、相机、字幕、板书、任务和学习记录。只在隐私页写一句“数据仅保存在本机”并不够工程上必须能回答每一种输入何时采集、在哪里处理、是否落盘、谁能导出、如何删除以及错误日志里会不会留下内容。听见课堂把“本机优先”拆成一组可检查的约束权限按功能触发原始音频默认不落盘结构化字幕/板书/任务进入本机 RelationalStore导出由用户主动复制 JSON删除需要二次确认并用事务清理核心表。本文从 P01 隐私说明、P11 设置与隐私、module.json5、ClassroomService.exportClassroomData()和 Repository 实现逐段还原这条数据流。一、本机优先先回答六个问题设计数据流前不妨对每类数据做一次六问来源是什么麦克风、相机、图库、用户编辑还是模型推断何时开始打开应用、进入页面还是用户点击开始处理在哪里内存、系统 Kit、端侧模型、本机数据库还是网络保存什么原始媒体、临时 URI、结构化文本还是状态值谁能带走后台自动上传、系统分享还是用户主动导出如何消失暂停、资源释放、单项删除、全部删除还是卸载应用。“本机优先”不是所有数据永远不流动而是默认最小化采集和持久化把跨边界动作交给用户明确触发并让删除路径真实可用。二、Manifest 声明权限不等于启动时立即索取项目的entry/src/main/module.json5只声明了当前核心能力需要的麦克风和相机权限并把使用场景限定为EntryAbility运行期间requestPermissions: [ { name: ohos.permission.MICROPHONE, reason: $string:microphone_permission_reason, usedScene: { abilities: [EntryAbility], when: inuse } }, { name: ohos.permission.CAMERA, reason: $string:camera_permission_reason, usedScene: { abilities: [EntryAbility], when: inuse } } ]声明只说明应用可能使用该能力。真正的隐私体验还取决于申请时机。P01 明确告诉用户麦克风“开始时询问”、相机“使用时询问”实时字幕也只有在用户点击开始、页面取得UIAbilityContext后才调用liveService.start()。这避免了两个常见问题用户刚打开应用就面对无法理解的权限弹窗用户拒绝后整个应用都不可使用。三、拒绝权限必须有可用状态而不是死循环弹窗实时字幕把permission_denied、unsupported和error设计为独立会话状态。权限被拒绝时主按钮显示“授权”页面展示原因和“前往系统设置”用户也仍可浏览已有字幕、板书和任务。if(this.liveStateLiveSessionState.PERMISSION_DENIED){awaitthis.recoverLiveMicrophonePermission();return;}consthostContext:Context|undefinedthis.getUIContext().getHostContext();if(hostContextundefined){this.liveFeedback无法取得页面上下文暂时不能申请麦克风权限。;this.liveStateLiveSessionState.ERROR;return;}在设置页点击“管理”只更新权限状态不会自动开始听写。这个细节能防止用户只是想检查授权却意外触发录音采集。隐私设计的正确目标不是让用户尽量授权而是让“不同意”成为一条稳定、可理解、可恢复的产品路径。四、按数据类型决定生命周期项目没有把所有输入一股脑写进数据库而是根据可追溯性与隐私成本分别处理数据处理与保存策略用户控制原始麦克风音频当前版本用于会话处理不创建录音文件可暂停/结束“保存原始音频”开关不可用且保持关闭实时字幕结构化为时间、说话人、文本、重点状态结束课堂后保存到本机可导出、可删除拍照/选图通过 CameraPicker/PhotoViewPicker 取得输入并进行解码/OCR由用户主动发起图片与 OCR 结果进入复核流程板书 OCR保存复核后的结构化文字、置信度和来源用户校对后写入可导出、可删除AI 任务候选保存标题、截止时间、来源与确认状态编辑、拒绝、确认未确认不进任务中心正式任务RelationalStore 中的已确认结构化记录完成、恢复、撤销确认、导出、删除日志记录生命周期、状态和错误摘要不应写入原始音频、图片字节、字幕全文或密钥不同生命周期能减少“为了以后可能有用”而长期保留原始材料的冲动。五、原始音频默认不落盘要成为可验证事实UserPreferences中虽然存在keepRawAudio默认值为falseP01 与 P11 的开关都处于不可用状态并清楚显示“当前版本不创建录音文件 · 默认关闭 · 0 B”。这不是一个尚未实现的可选功能而是当前版本的隐私边界。导出模型又用rawAudioIncluded单独声明是否包含原始音频constpayload:ClassroomDataExportnewClassroomDataExport(2,newDate().toISOString(),this.storageMode,course,transcript,scans,tasks,false);returnJSON.stringify(payload);最后一个参数固定为false设置页也回读提示“不包含原始音频”。若未来真的增加录音保存不能只把开关改成可点。还必须补齐保存目录、文件生命周期、容量上限、加密、后台行为、删除覆盖范围、导出策略、权限说明和版本迁移再单独进行隐私审查。六、结构化结果进入本机 RelationalStore应用启动时EntryAbility先初始化课堂数据成功时使用RelationalClassroomRepository失败则保留内存降级。数据库名为heard_classroom.db安全等级配置为S2。四类核心表的职责清晰表保存内容不保存内容courses课程标题、教师、教室、时间、进度、颜色麦克风或图片原始流transcript_segments时间、说话人、字幕文本、重点标记原始音频帧scan_notes板书标题、复核文本、置信度、来源图片字节tasks标题、截止时间、来源、确认与完成状态模型中间张量这套设计保留了用户真正需要回顾和行动的结构化内容同时避免把所有原始输入永久化。需要注意的是“本机保存”不自动等于“零风险”设备解锁、备份策略、剪贴板和调试日志仍是独立边界不能因为使用了 RelationalStore 就停止审查。七、导出必须由用户主动触发并让内容可检查P11 的“导出 JSON 到剪贴板”不会在后台定时执行。用户点击后页面调用 Service 聚合课程、字幕、板书和任务构造带schemaVersion、exportedAt、storageMode的 JSON再写入系统剪贴板constexportText:stringawaitthis.service.exportClassroomData();constdata:pasteboard.PasteDatapasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN,exportText);awaitpasteboard.getSystemPasteboard().setData(data);this.notice课堂数据已导出为 JSON 并复制到剪贴板不包含原始音频;这种设计的优点是导出格式透明、可被用户检查也方便备份与迁移。风险是剪贴板不属于应用私有存储用户复制后内容可能被粘贴到其他应用部分系统环境也可能展示剪贴板历史。因此生产版应继续补充导出前显示数据范围、敏感提示和记录数量必要时改用受控文件并允许用户选择保存位置失败时保证课堂数据不变绝不在后台静默复制。八、删除全部数据需要二次确认与事务删除入口先检查是否有数据再进入确认态真正删除只有在allDataDeletionConfirmation为真且当前没有其他数据操作时才执行。成功后还会重置实时会话、候选隐藏态、编辑态、最近确认 ID 和各页面选中证据再重新读取数据。Repository 对四张核心表使用同一个事务store.beginTransaction();try{awaitstore.executeSql(DELETE FROM${TABLE_TASKS});awaitstore.executeSql(DELETE FROM${TABLE_SCANS});awaitstore.executeSql(DELETE FROM${TABLE_TRANSCRIPT});awaitstore.executeSql(DELETE FROM${TABLE_COURSES});store.commit();returntrue;}catch(error){store.rollBack();thrownewError(Failed to clear classroom data.);}事务保证不会只删掉任务却留下字幕或只删课程导致其他表成为孤立记录。UI 同时明确说明课程、字幕、板书和任务将永久删除设置偏好保留系统权限和应用外文件不受影响。这里也要诚实区分“数据库删除”和“设备级不可恢复擦除”。当前实现能证明核心表记录被事务删除不能自动证明底层闪存块被安全覆盖也不能删除用户已经复制到剪贴板或导出到应用外的副本。九、日志只记录状态不记录课堂内容隐私数据很容易从一条调试日志泄漏。项目当前可见的hilog调用主要记录 Ability 生命周期、数据初始化模式、页面加载、权限结果、相机/骨骼能力状态和错误摘要没有主动格式化字幕全文、OCR 文本、图片字节或任务内容。团队仍应建立强制规则不输出原始音频缓冲区或其 Base64不输出图片 URI、图片字节和完整 OCR 结果不输出字幕全文、任务标题、用户输入和剪贴板内容不输出 token、证书密码、签名材料或设备身份数据生产构建对错误对象做白名单映射避免三方异常夹带敏感字段调试问题使用记录数量、状态枚举、耗时和匿名 ID而不是内容本身。“代码里没看到console.log(text)”只是起点。每次引入新 Kit、Provider 或网络 SDK都要重新审计它的默认日志行为。十、没有上传代码不等于可以写“永不联网”当前module.json5的权限清单只看到麦克风和相机课堂 Repository 也是本机 RelationalStore/内存实现P11 文案说明课堂内容不在后台上传。基于现有代码可以说“当前课堂数据主链路没有实现后台上传”。但不宜把它扩大成“应用任何情况下绝不联网”系统 Kit 的实现条件、未来依赖、应用更新、崩溃分析或后续云同步都可能改变边界。更严谨的公开表述是当前版本的课堂字幕、板书和任务由本机 Repository 保存应用未实现课堂内容后台上传导出只在用户点击后发生且不包含原始音频。如果未来增加云同步必须把网络 Repository 作为新的数据源边界补充服务端合约、鉴权、传输加密、删除同步、冲突处理、未成年人/教育场景合规和关闭后的本机降级而不是在现有 Service 里悄悄发请求。十一、从 P01 到 P11要让承诺前后一致隐私页负责“采集前说明”设置页负责“使用中管理与退出”。两页承诺必须由同一组真实能力支撑P01 的承诺P11 的控制工程实现点击开始后才申请麦克风权限状态与管理入口LiveTranscriptionService状态机原始音频默认不保存不可用的关闭开关、0 BkeepRawAudio false、导出标记false结构化结果仅写入本机本机记录数量与存储模式RelationalClassroomRepository可暂停、拒绝权限授权恢复但不自动开始permission_denied与恢复路径可删除全部课堂数据二次确认、取消、永久删除Repository 事务清表如果 P01 写了“随时删除”P11 就不能只放一个无效按钮如果导出实际包含音频P01 也不能继续显示“0 B”。隐私文案不是营销层它是需要被代码、状态和回读共同验证的产品合约。十二、验收本机优先数据流的最小清单发布前至少应做以下验证首次打开不主动申请麦克风或相机点击实时字幕后才进入麦克风授权拒绝权限后页面可继续使用并能前往系统设置恢复权限管理不会自动开始听写暂停、结束、后台切换后采集资源正确释放文件系统中没有新建原始录音文件导出 JSON 的rawAudioIncluded为false导出只包含约定的结构化字段失败不修改数据库删除前必须二次确认取消后数据保持不变删除成功后课程、字幕、板书、任务都为 0重启也不恢复种子数据设置偏好按产品约定保留应用外文件和系统权限不被误删hilog 中不出现真实字幕、OCR 全文、图片 URI、密钥或导出 JSON。其中“没有录音文件”“重启不恢复”“日志无敏感内容”都需要真机文件、重启和日志回读不能只看代码下结论。十三、当前项目的已验证与未验证边界从当前源码可以确认麦克风和相机权限用途已声明页面按功能触发对应流程隐私说明、拒绝状态、授权恢复和设置管理形成可见路径原始音频保存开关默认关闭且不可用导出明确标记不含原始音频课程、字幕、板书、任务由 RelationalStore/内存降级 Repository 管理JSON 导出由用户点击触发全部删除经过二次确认和数据库事务当前课堂主链路没有实现后台上传。仍不能仅凭代码宣称所有目标设备都不会产生临时媒体文件、底层删除达到物理不可恢复、所有系统 Kit 永不联网、所有异常对象都绝不夹带敏感字段。它们需要目标设备、系统版本、真实权限分支、文件系统和 hilog 的运行验收。总结本机优先是一条端到端数据流而不是页面上的一句口号。它从采集前说明开始经由按需权限、内存处理、结构化本机存储、人工确认、主动导出、二次确认删除和日志脱敏最终让用户知道数据在哪里、何时产生、怎样带走、如何清理。听见课堂当前最清晰的边界是原始音频默认不落盘课堂结构化记录进入本机 RepositoryAI 候选未经确认不进入正式任务中心导出和删除都由用户触发。下一篇将进入实时字幕会话状态机讨论麦克风权限、开始、暂停、继续、结束和异常恢复如何保持一致。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →