React Native Keychain 安全存储封装实战:从原理到完整实现
1. 先把目标说清楚为什么非要封装一层做移动端开发的人对“敏感信息存哪”这个问题多少都纠结过。登录态 token、刷新令牌、设备指纹、支付凭证这些数据只要丢一份到明文里心里就一直不踏实。React Native 项目里最常见的做法是直接往 AsyncStorage 里塞图省事。但 AsyncStorage 本质就是本地文件读写存下来的 token 在设备上等同于明文暴露一旦 APP 沙盒被穿透或者用户手机做了备份提取数据等于裸奔。这也是我这次下定决心把 React Native Keychain 做一次完整封装的核心原因。Keychain 本身是 iOS 提供的安全存储能力能让敏感数据在系统安全模块的保护下写入设备而不只是“放在沙盒目录里”。但 React Native 业务代码没法直接碰原生安全框架需要经过一层桥接。而这层桥接如果只是简单暴露几个方法业务侧还是容易乱用比如有人把大对象硬塞进去、有人没配访问级别、有人不知道该在什么时机调用。所以“完整封装”要做的事不只是“能存能取”而是把安全策略、数据格式、错误处理、平台差异全收敛到同一套 API 里。这篇内容适合正在处理登录态持久化、敏感配置存储或者准备把 Keychain 用进现有 RN 项目的开发者。我会从原理聊到选型再给出完整封装代码与踩坑记录尽量让你看完可以直接照搬。这篇不是 SDK 文档复述是我自己在项目里把 Keychain 从裸调用到完整封装的全过程记录里面不少坑网上资料很少提到。2. 动手前必懂Keychain 的原理和选型2.1 理解 Keychain 里的关键概念Keychain 表面的 API 就是增删改查但背后有几个概念直接决定封装是否合理。第一是 service 和 account 的维度。Keychain 存一条记录时至少需要服务名service和账号account两个字段来定位。service 类似“命名空间”account 类似“这条数据在命名空间里的键名”。比如登录 token 可以 service 叫auth、account 叫access_token设备唯一标识可以 service 叫device、account 叫uuid。我见过不少项目图省事所有数据都用同一个 service 同一个 account 去覆盖结果不同业务互相踢数据。封装里的第一个原则就是统一生成命名空间业务方只传一个 key内部自动补 service。第二是 accessible 访问级别。它决定数据“什么时候允许被读”。比如WhenUnlocked表示解锁期间可访问锁屏后应用在后台就无法读取AfterFirstUnlock表示设备首次解锁后就能一直读适合需要在后台刷新的场景ThisDeviceOnly后缀则表示数据不随备份迁移设备换机后读不到。很多人封装时根本不传这一项默认值在部分 iOS 版本上可能并不是最安全的选择。我在封装层把访问级别变成配置项由初始化参数统一控制。第三是 kSecClass。大部分场景用的是GenericPassword。如果你要存小证书、对称密钥会有更专用的类型但对 React Native 业务应用来说GenericPassword 基本通吃。封装层可以先把范围锁定在 GenericPassword避免把原生复杂度全暴露出来。2.2 选型原生手写还是第三方库完整封装的前提是“底座”。三种常见路线的差异我直接列个表说明方案优点缺点适用场景手写 Swift/ObjC 桥接完全可控不依赖第三方要维护原生代码工作量大Android 还得另写一套团队原生能力强有特殊安全需求expo-secure-storeExpo 生态内使用简单脱离 Expo Go 要重新配置API 较薄Expo 项目react-native-keychain封装了原生细节跨双端API 易懂参数语义要理解部分高级安全能力暴露不完整绝大多数 RN 项目我的结论很清楚把第三方库当原生底座把自研逻辑全部放到 JS 层。react-native-keychain 已经处理好了 SecItem 调用、Android Keystore 兼容、生物识别选项等底层细节我与其再花几周去重复造轮子不如把精力花在业务层更重要的“封装”上。最终形成的结构是业务代码只依赖我自己定义的SecureStore类不直接接触库 API未来底座从 A 库换成 B 库业务侧零改动。2.3 模块划分让封装有边界我建议把工程分为三层。最底层叫 StoreProvider只做一件事对接具体库提供setItem、getItem、removeItem三个原子操作。中间层叫 SecureStore负责序列化、命名空间、错误码转换、安全降级。最上层是业务调用只关心set(access_token, token)这样的语义化方法。分层还有个额外好处就是单元测试容易写。StoreProvider 可以 mockSecureStore 的策略逻辑可以单独验证。我当时在项目里最头疼的“数据被覆盖”“错误原因看不懂”“Android 上突然抛异常”这三个问题全是因为早期没有分层逻辑散落在业务代码里。重新按这个结构整理后问题定位效率高了很多。3. 封装设计API、类型和序列化3.1 对外 API 怎么设计才顺手一个安全存储模块API 不需要花哨但必须稳定。我最终沉淀下来的是下面这几个方法export class SecureStore { static async set(key: string, value: unknown, options?: SecureStoreOptions): Promisevoid; static async getT(key: string): PromiseT | null; static async remove(key: string): Promisevoid; static async contains(key: string): Promiseboolean; static async resetNamespace(ns?: string): Promisevoid; static async getKeys(): Promisestring[]; }这里解释两个容易想多了的设计点。第一为什么set要接unknown而不是string。因为业务方经常会存一个对象比如用户资料里的昵称和头像 URL。如果 API 只支持字符串那业务侧每次都要自己 JSON.stringify忘了就是[object Object]。我在封装层内部统一做序列化对调用方来说是“传什么都能存”内部再判断类型和大小。第二为什么有resetNamespace而不是clearAll。因为 Keychain 的数据是跨应用卸载存续的一个“全部清空”搞不好把别的模块的数据也删了。用命名空间去做清空每次只清理当前业务域的数据安全性高很多。实际使用中“退出登录清缓存”这个场景一个resetNamespace(auth)就够了。3.2 类型系统与序列化策略TypeScript 带来的一个重要收益是调用方不需要记“这个 key 返回的是什么类型”。getT配合泛型声明一次就能在调用处拿到类型推导interface UserProfile { nickname: string; avatar: string; } const profile await SecureStore.getUserProfile(user_profile);序列化这块要注意两个地方。第一undefined和null必须区分。null表示“存过但没值”undefined表示“设计上就不该存在”。我在set里做了约束存undefined直接抛错引导调用方改用remove。这样可以避免很多“删不掉”“读出来是 undefined”的边界问题。第二大对象必须做拆包提示。单条 Keychain 记录不适合搞定 KB 级以上的数据。我在封装里加了一个阈值检查超过某个体积就先尝试压缩 JSON压缩后还超限就抛明确错误。相比让业务方在运行时看到一个-34018类型的原生错误码这个提示友好得多。const MAX_RAW_LENGTH 2048; function serialize(value: unknown): string { if (value null || value undefined) { throw new Error(SecureStore: 不允许存储 null/undefined请改用 remove); } const raw typeof value string ? value : JSON.stringify(value); if (raw.length MAX_RAW_LENGTH) { // 可在此处做压缩或抛提示让调用方拆分 throw new Error(SecureStore: 单条数据超过 ${MAX_RAW_LENGTH} 字符); } return raw; }3.3 错误处理让原生错误码变成人话原生 Keychain 返回的错误码业务开发根本不想看。我的做法是做一个错误码映射把常见情况转成业务可理解的错误类型export enum SecureStoreErrorCode { ITEM_NOT_FOUND ITEM_NOT_FOUND, AUTH_FAILED AUTH_FAILED, USER_CANCELED USER_CANCELED, INTERACTION_NOT_ALLOWED INTERACTION_NOT_ALLOWED, LIMIT_EXCEEDED LIMIT_EXCEEDED, UNKNOWN UNKNOWN, }映射逻辑很简单读库抛出来的底层错误我先判断是不是“找不到”是就直接转ITEM_NOT_FOUND上层把它当成正常分支处理不要打错误日志而AUTH_FAILED、USER_CANCELED这种涉及用户交互的单独提示不让业务侧误判为系统故障。这里有个容易踩的雷带生物识别保护的 Keychain 项在模拟器上经常会报-25308Interaction Not Allowed如果你不做映射测试同学就会把这个当成必现 bug 提上来。4. 完整实操从原生桥接到 JS 调用4.1 环境准备与依赖注入先做依赖安装。这里以苹果生态为基础Android 可以视为同一套抽象npm install react-native-keychain cd ios pod install如果不开 Keychain Sharing 能力默认单应用沙盒模式下是可以直接用的。只有当你需要“多个 App 共享同一份 Keychain 数据”或者“App 与 Extension 共享登录态”时才需要去 target 的 Signing Capabilities 里打开 Keychain Sharing并配置相同的 access group。大多数人用不到但这个开关在封装层可以预留一个accessGroup参数方便以后接 widgets 或 App Clip。我自己搭的项目结构长这样src/ services/ secureStore/ SecureStore.ts // 对外统一门面 SecureStoreProvider.ts // 对接 react-native-keychain SecureStoreError.ts // 错误码定义与映射 SecureStoreOptions.ts // 初始化配置类型再看一眼SecureStoreProvider它的作用就是兜住底层异常把 SDK 抛出的东西转换成上面说的错误类型import * as Keychain from react-native-keychain; export class SecureStoreProvider { async setItem(service: string, key: string, value: string) { try { await Keychain.setGenericPassword(key, value, { service }); } catch (e) { throw SecureStoreError.fromNative(e); } } async getItem(service: string, key: string): Promisestring | null { try { const res await Keychain.getGenericPassword({ service }); if (!res) return null; return res.password; } catch (e) { throw SecureStoreError.fromNative(e); } } }4.2 手写原生桥接的参考实现选读如果你的团队决定不走第三方依赖下面这段 Swift 可以当作最简参考。它基于系统 Security 框架完成了 set/get/delete 三个操作。注意这不是完整生产级代码只是帮你理解原生层到底做了什么。import Security objc(KeychainBridge) class KeychainBridge: NSObject { objc func set( _ key: String, value: String, service: String, resolver: escaping RCTPromiseResolveBlock, rejecter: escaping RCTPromiseRejectBlock ) { let data Data(value.utf8) let query: [CFString: Any] [ kSecClass: kSecClassGenericPassword, kSecAttrService: service, kSecAttrAccount: key, kSecValueData: data, kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] SecItemDelete(query as CFDictionary) let status SecItemAdd(query as CFDictionary, nil) if status errSecSuccess { resolver(true) } else { rejecter(KEYCHAIN_ERROR, SecItemAdd failed: \(status), nil) } } }原生代码的难点主要不在增删改查而是访问控制SecAccessControl的创建、生物识别 context 的注入、后台访问的时机判断。小团队自己写很容易埋坑这也是我最终选第三方底座的原因。如果你坚持自己写至少要把kSecAttrAccessible作为可配置项而不是写死。4.3 JS 层完整封装实现下面是SecureStore的核心实现。我刻意让代码保持单文件可见方便你直接移植。import { Platform } from react-native; import * as Keychain from react-native-keychain; import { SecureStoreProvider } from ./SecureStoreProvider; import { SecureStoreError, SecureStoreErrorCode } from ./SecureStoreError; export interface SecureStoreOptions { servicePrefix?: string; accessible?: number; accessGroup?: string; } export class SecureStore { private static provider new SecureStoreProvider(); private static defaultServicePrefix com.app.securestore; private static buildService(ns?: string): string { const prefix this.defaultServicePrefix; return ns ? ${prefix}.${ns} : prefix; } static async set(key: string, value: unknown, ns?: string): Promisevoid { if (value null || value undefined) { throw new SecureStoreError( SecureStoreErrorCode.INTERACTION_NOT_ALLOWED, SecureStore: 不支持存储 null/undefined请使用 remove 删除 ); } const serialized this.serialize(value); await this.provider.setItem(this.buildService(ns), key, serialized); } static async getT(key: string, ns?: string): PromiseT | null { const raw await this.provider.getItem(this.buildService(ns), key); if (raw null) return null; return this.deserializeT(raw); } static async remove(key: string, ns?: string): Promisevoid { await this.provider.removeItem(this.buildService(ns), key); } private static serialize(value: unknown): string { if (typeof value string) return value; return JSON.stringify(value); } private static deserializeT(raw: string): T { try { return JSON.parse(raw) as T; } catch { return raw as unknown as T; } } }这段代码里有几个细节展开了说。字符串优先原样存不做二次 JSON 转义是为了兼容旧数据。我遇到过一种场景老代码把hello整个字符串用JSON.stringify塞了进去存进去带了一对引号新的封装如果统一做 parse历史数据会被换成hello虽然看起来没错但可能有兼容风险。所以序列化时对 string 类型特殊处理反序列化时先尝试 parse失败就按原始字符串返回。buildService里的前缀我做成内部默认配置不让业务传一个完整的随机字符串。不同业务模块用不同 namespace比如登录态统一传auth缓存 passcode 用local_pin。集成后调用方写起来非常轻await SecureStore.set(access_token, token, auth); const token await SecureStore.getstring(access_token, auth);4.4 关键配置访问级别和生物识别把react-native-keychain的 accessible 参数设为可选之后默认值我不建议用原生默认项而应显式指定。const DEFAULT_KEYCHAIN_OPTIONS { accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY, // 可选每次读取时校验 Face ID / Touch ID // accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET, };这里面的逻辑是登录 token 这种数据设备锁屏后应用进入后台时的读取需求很少用WHEN_UNLOCKED_THIS_DEVICE_ONLY已经足够而且ThisDeviceOnly保证备份恢复后数据不泄漏到新设备。如果 App 要做后台刷新就需要AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY否则后台任务读不到 token 会静默失败。生物识别我单独做了封装入口叫setBiometricProtected。它内部走getSupportedBiometryType做能力检查再决定是否把accessControl设为BIOMETRY_CURRENT_SET。这看起来只是传参数的区别实际差别很大普通 Keychain 项在系统弹窗验证后直接返回生物识别项则需要先通过 Face ID/Touch ID业务代码必须能感知“用户取消验证”这个分支。5. 安全与多端一致性这些细节不能省5.1 访问等级选择指南封装做得再好参数配错全白费。我整理了一张表基本覆盖日常场景使用场景推荐访问级别原因登录 access_tokenWHEN_UNLOCKED_THIS_DEVICE_ONLY锁屏后不需要后台读取数据不迁移刷新令牌AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY后台刷新任务需要读且不迁移支付 PINWHEN_UNLOCKED_THIS_DEVICE_ONLY BIOMETRY敏感数据要额外认证设备唯一标识ALWAYS_THIS_DEVICE_ONLY如系统支持需要长期稳定存在注意ALWAYS系列在新系统上已弱化不应在新代码里主动选用。Apple 从 iOS 13 左右开始逐步收紧后台访问的权限如果你发现锁屏后读取被拒多半是访问级别选高了优先按上表调参。5.2 多账号和多模块隔离Keychain 在同一个应用里是全局共享的不隔离就会互相覆盖。我在封装层做的隔离维度有两个模块维度用 namespace账号维度用 key 前缀。比如一个应用支持多账号登录存储时 key 不能只叫user_info而应该按account_${userId}_user_info来设计。如果直接把 account 落在 key 上退出一个账号去登录另一个账号时缓存的数据很容易串。更合适的做法是SecureStore.set(user_info, data,account_${userId})或对 key 做规则约束二选一即可但核心是必须让业务侧明确“这个数据属于哪个账号”。实践建议在团队内部把命名规则写进文档比在代码里写死更省事。命名规范建议形如业务域:账号域:实际key。5.3 Android 侧的兼容差异处理虽然标题叫 Keychain但我做封装的接口是跨平台的。react-native-keychain 在 Android 上的实现走的其实是 Keystore 加密后再落到本地存储行为与 iOS 有差异。具体来说iOS 的 Keychain 数据在应用卸载后仍可能保留Android 侧往往随应用卸载一并清掉。iOS 有明确的 accessible 语义Android 侧更多依赖 Keystore 的解锁状态和加密偏好设置。生物识别在 Android 上需要额外声明指纹权限并在部分厂商 ROM 上有兼容问题。所以封装层暴露的 API 必须是平台无关的但内部要做Platform.OS判断对 Android 降级处理。我在get方法里加了一个开关默认 iOS 走严格模式Android 走兼容模式避免因为平台差异导致业务报错。5.4 数据迁移策略版本升级过程中Keychain 数据可能面临“已保存数据读不出来”的情况原因多半不是我说的“丢数据”而是 service 名字变了。旧版本用com.app.old新版本改成com.app.newKeychain 里当然读不到。解决办法是在初始化时提供migrateFrom参数。读新 service 没数据时先尝试读旧 service读到了就自动搬到新 service 并删旧数据。这个迁移过程只在首次启动时执行一次const oldValue await provider.getItem(com.app.old, key); if (oldValue ! null) { await provider.setItem(com.app.new, key, oldValue); await provider.removeItem(com.app.old, key); }6. 踩坑实录我在封装过程中遇到的问题6.1 模拟器上的弹窗循环灾难在 iOS 模拟器上测试带生物识别的 Keychain 项系统会不断弹出“输入密码以启用 Face ID/Touch ID”的授权框而且每次读取都会再次弹。原因很简单模拟器没有真实生物硬件系统只能退化到密码/授权模式。我的处理方法是在封装层增加isSupported的检查模拟器环境下自动跳过生物识别参数只使用普通访问级别。测试环境用__DEV__判断生产环境再按真实设置走。避免弹窗的另一个关键点是不要每次读取都重新创建accessControl复用一份配置可以减少系统额外校验的发生频率。6.2 应用卸载之后数据还在iOS 的 Keychain 数据不会随 App 卸载删除这是特性也是坑。如果你的 App 登录态存在 Keychain 里用户卸载重装后打开理论上还能读到旧 token有可能出现“未登录却进入了已登录状态”的诡异情况。我在封装层做了解耦敏感 token 存 Keychain但“是否曾经登录过”这个标志位放一个普通持久化存储。重装后标志位丢失业务侧自动走“需要重新登录”的流程然后再清一遍 Keychain 里的残留数据。这种双标记法在真实项目里很实用。6.3 后台访问被拒读不到数据的静默失败有一次排查一个“App 进后台再回来 token 自动失效”的问题查了很久发现不是 token 过期而是后台状态下读 Keychain 抛了kSecInteractionNotAllowed。因为当时用的访问级别是WhenUnlocked锁屏后系统直接拒绝访问。后来把访问级别调整到AfterFirstUnlock并把“禁止后台读取”作为默认约束重新写了一版。这里分享一个自查顺序先确认设备是否已解锁再确认访问级别是否覆盖这个场景最后看有没有开启扩展如 Apple Watch 或 Widget造成的上下文切换。6.4 常见错误码速查表基于实际经历我把最常见的几个错误码整理成表格方便你定位问题错误码含义常见原因处理建议0 (errSecSuccess)成功无无-25299 (errSecDuplicateItem)重复写入已存在同 serviceaccount先删再写或 use update-25300 (errSecItemNotFound)记录不存在数据被清或 key 拼错按空值处理不抛异常-25308 (errSecInteractionNotAllowed)当前不允许访问锁屏状态/未解锁调整访问级别或重试时机-34018 (errSecMissingEntitlement)缺少权限配置Keychain Sharing 未开启检查 entitlements 文件-128 (userCanceled)用户取消生物识别取消按取消分支处理6.5 踩过几次坑之后的一些体感封装这件事真正难的不是代码而是设计决策。我最大体会是不要让业务侧直接接触底层参数不然每次接入一个新业务模块就要重新解释一遍“什么是 access group”“Android 为什么不完全一致”。有了统一的SecureStore门面之后团队里新来的同学十分钟就能上手。另外一个值得提的点是日志。Keychain 操作不能打明文敏感数据但可以打操作结果和时间。我看过太多线上问题排查不了的案例都是因为关键安全操作一点日志都没留。我在封装层加了__DEV__环境下打 key 名和操作结果的逻辑生产只统计成功失败率。这样既保安全也不耽误出问题时的定位效率。最后想说的是安全存储这个方向没有银弹。Keychain 解决的是“静态存储”的敏感数据泄露问题它管不了逆向调试、管不了网络层被抓包、也管不了业务层主动把 token 传给第三方。完整封装能帮团队把“安全存储”这件事的复杂度收口到一个模块让上层业务写起来像用普通缓存一样简单同时底线不丢。

相关新闻

外延生长缺陷控制:层错与位错的来源分析

外延生长缺陷控制:层错与位错的来源分析

一、痛点背景:从一次真实的生产事故说起外延生长缺陷控制:层错与位错的来源分析这个问题,在FAB里不是一天两天了。我见过太多工程师踩坑:要么是方法用错导致数据误判,要么是工具选型失误导致项目延期,要么是…

2026/10/17 14:03:49 阅读更多 →
OFD导入实战:版式转流式文档的核心技术与实现方案

OFD导入实战:版式转流式文档的核心技术与实现方案

最近在给单位的信创办公系统做OFD导入功能,踩了不少坑,也总结了一些可行方案。这个需求听起来很简单——用户在编辑文档时能直接导入OFD文件,但实际上它横跨了格式解析、版面还原、流式编辑三大块,牵扯到的细节比预想多得多。如果…

2026/10/17 12:58:09 阅读更多 →
Flutter在OpenHarmony上的录音按钮状态机与事件通道实践

Flutter在OpenHarmony上的录音按钮状态机与事件通道实践

去年底接了个需求:在一台 OpenHarmony 开发板上做一款带录音功能的音乐播放器。用户的典型场景是播放一首歌、跟唱一段,录下来再回放对比。项目本身不复杂,难就难在团队没人写过鸿蒙原生,时间又压得紧,最终技术选型落在…

2026/10/17 20:03:39 阅读更多 →

最新新闻

中文简历怎么喂:把AI求职工具的语义匹配调顺,先做对三件事

中文简历怎么喂:把AI求职工具的语义匹配调顺,先做对三件事

中文简历怎么喂:把AI求职工具的语义匹配调顺,先做对三件事 【免费下载链接】ai-job-search The job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep…

2026/10/17 21:35:40 阅读更多 →
Kafka接入AI的两种落地路径:数据管道与智能运维实战

Kafka接入AI的两种落地路径:数据管道与智能运维实战

1. 为什么说这次接入不只是“凑热点”:两个方向同时成立自从“Kafka已正式接入AI”这句话在公司群里出现,我第一反应并不是兴奋,而是先确认了一件事:这个“接入”到底指哪个方向。因为以我多年维护Kafka集群的经验,这句…

2026/10/17 21:35:40 阅读更多 →
揭秘Claude Code的PromptSuggestion:智能提示与推测执行如何降低AI工具延迟

揭秘Claude Code的PromptSuggestion:智能提示与推测执行如何降低AI工具延迟

把 Claude Code 的源码从外到内翻过一轮之后,我印象最深的不是模型调用封装,也不是终端 UI 渲染,而是 PromptSuggestion 这个模块。模块一第五节把“智能提示”和“推测执行”绑在一起讲,初看只是给用户补几个命令,深入…

2026/10/17 21:35:40 阅读更多 →
打造交易日自动化研究助手:WorkBuddy接入zhengxi-views技能的完整指南

打造交易日自动化研究助手:WorkBuddy接入zhengxi-views技能的完整指南

AI 技能金融科技AI 评测 【免费下载链接】zhengxi-views 可溯源的郑希(易方达基金经理)投研 Agent Skill——基于他全部公开观点原文 有原话佐证的投资方法 全市场基金真实数据,能溯源问答、按他框架给基金打分,绝不杜撰。⚠️仅研究学习辅助&#xff…

2026/10/17 21:34:40 阅读更多 →
PHP分库分表实战:从拆分策略到数据迁移的完整指南

PHP分库分表实战:从拆分策略到数据迁移的完整指南

1. 分库分表到底解决的是什么问题先聊几句实在的。做过几年PHP后端的人,多半会遇到这么个场景:某天线上报警,数据库连接数打满,慢查询日志一拉一大片。查了半天,发现不是代码写崩了,而是单表数据量太大&…

2026/10/17 21:33:39 阅读更多 →
Flutter鸿蒙跨平台开发实战:星座运势App完整适配指南

Flutter鸿蒙跨平台开发实战:星座运势App完整适配指南

那段时间我正好在做一个"多端一致"的内部工具类项目,需求方点名要覆盖鸿蒙生态,同时又不希望Android、iOS各养一套原生团队。我当时第一反应就是把Flutter的跨平台方案直接搬到鸿蒙上试试水。为了验证这条路走得通,我顺手做了一个&…

2026/10/17 21:33:39 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →