From 4c3019edab626df43c7f11c6d8d176923cf7cdee Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Tue, 16 Jun 2026 12:14:52 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20CLAUDE.md=20+=20?= =?UTF-8?q?=E5=90=84=20package=20README=20+=20=E6=9E=B6=E6=9E=84=E6=80=BB?= =?UTF-8?q?=E8=A7=88=20+=20=E6=A8=A1=E5=9D=97=E5=BE=85=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent 8 文档生成: - CLAUDE.md 项目上下文 - 各 package README (common/update/push/im/xwebview/license/log) - docs/架构总览.md - docs/模块待开发说明.md --- CLAUDE.md | 83 ++++++++++++----------- docs/架构总览.md | 105 +++++++++++++++++++++++++++++ docs/模块待开发说明.md | 85 ++++++++++++++++++++++++ packages/common/README.md | 109 +++++++++++++++++++++++++++++++ packages/im/README.md | 127 ++++++++++++++++++++++++++++++++++++ packages/license/README.md | 59 +++++++++++++++++ packages/log/README.md | 89 +++++++++++++++++++++++++ packages/push/README.md | 53 +++++++++++++++ packages/update/README.md | 116 ++++++++++++++++++++++++++++++++ packages/xwebview/README.md | 78 ++++++++++++++++++++++ 10 files changed, 867 insertions(+), 37 deletions(-) create mode 100644 docs/架构总览.md create mode 100644 docs/模块待开发说明.md create mode 100644 packages/common/README.md create mode 100644 packages/im/README.md create mode 100644 packages/license/README.md create mode 100644 packages/log/README.md create mode 100644 packages/push/README.md create mode 100644 packages/update/README.md create mode 100644 packages/xwebview/README.md diff --git a/CLAUDE.md b/CLAUDE.md index 1ec436c..6d06c3d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,84 +2,89 @@ ## 项目定位 -XuqmGroup React Native SDK monorepo。为集成宿主 App(如 YiwangxinApp4)提供核心能力:初始化、HTTP、设备信息、XWebView、OTA 更新、推送、IM、证书、**日志追踪**。 +XuqmGroup React Native SDK monorepo。为集成宿主 App(如 YiwangxinApp4)提供核心能力:初始化、HTTP、设备信息、XWebView、OTA 更新、推送、IM、证书、日志追踪。 -- Git 远端:`ssh://git@xuqinmin.com:2222/`(以 `git remote -v` 实际结果为准) +- Git 远端:`https://xuqinmin.com/xuqmGroup/XuqmGroup-RNSDK.git` - Nexus npm 发布:`https://nexus.xuqinmin.com/repository/npm-hosted/` +- 规范文档:`XuqmGroup-Docs/design/06-sdk-cross-platform-spec.md` ## 包结构 -| 包名 | 目录 | 当前版本 | 状态 | -|------|------|----------|------| -| `@xuqm/rn-common` | packages/common | 0.4.0 | 活跃开发 | -| `@xuqm/rn-update` | packages/update | — | 活跃开发 | -| `@xuqm/rn-xwebview` | packages/xwebview | — | 活跃开发 | -| `@xuqm/rn-log` | packages/log | 0.1.0(新) | 新建中 | -| `@xuqm/rn-push` | packages/push | — | 代码冻结(仅文档) | -| `@xuqm/rn-im` | packages/im | — | 代码冻结(仅文档) | -| `@xuqm/rn-license` | packages/license | — | 代码冻结(仅文档) | +| 包名 | 目录 | 版本 | 说明 | +|------|------|------|------| +| `@xuqm/rn-common` | packages/common | 0.4.0 | 核心层:初始化、HTTP、设备、XWebView Bridge、API hooks | +| `@xuqm/rn-update` | packages/update | 0.4.0 | OTA 热更新、APK 更新 | +| `@xuqm/rn-push` | packages/push | 0.2.2 | 厂商推送(代码冻结,仅文档) | +| `@xuqm/rn-im` | packages/im | 0.2.2 | IM 会话、消息(代码冻结,仅文档) | +| `@xuqm/rn-xwebview` | packages/xwebview | 0.2.2 | WebView 容器 + JSBridge UI 组件 | +| `@xuqm/rn-license` | packages/license | 0.3.0 | 证书授权(代码冻结,仅文档) | +| `@xuqm/rn-log` | packages/log | 0.1.0 | 日志、错误追踪、漏斗分析(新) | **代码冻结模块(push/im/license):不修改代码,仅补充文档。** -## 技术约束 +## 技术栈 -- **React Native ≥ 0.76**(Bridgeless Mode,New Architecture) -- **TypeScript 5.x strict** — `noImplicitAny: true`,禁止 `any` -- **向下兼容**:公开接口不删除不修改,新字段一律可选 -- **无静默降级**:autoInit 失败时 `__DEV__` 模式 throw,生产模式 console.error +- React Native >= 0.76(Bridgeless Mode,New Architecture) +- TypeScript 5.x strict — `noImplicitAny: true`,禁止 `any` +- yarn workspaces monorepo +- 发布到 Nexus npm 私有仓库 -## 核心 API(@xuqm/rn-common) - -### 初始化(两种方式,均不得修改签名) +## 初始化(两种方式) **方式 A — 配置文件自动初始化(推荐)** -宿主配置 Babel alias `@xuqm/autoinit-config` → 加密配置文件。SDK 自动读取并初始化,App 代码无需调用任何 init。 +宿主配置 Babel alias `@xuqm/autoinit-config` → 加密配置文件(XUQM-CONFIG-V1 格式)。SDK 自动读取并初始化,App 无需调用任何 init。 **方式 B — 手动初始化** ```ts -// 仅传 appKey → 使用内置公有平台 +import { XuqmSDK } from '@xuqm/rn-common' + +// 公有平台 await XuqmSDK.initialize({ appKey: 'xxx' }) -// 传 appKey + platformUrl → 使用指定私有化平台 +// 私有化平台 await XuqmSDK.initialize({ appKey: 'xxx', platformUrl: 'https://xxx' }) // 等待异步平台配置拉取 await XuqmSDK.awaitInitialization() ``` -**两种平台互相独立,不允许自动降级到默认公有平台。** +**两种平台互相独立,不允许自动降级。** + +## 用户信息 -### 用户信息 ```ts XuqmSDK.setUserInfo({ userId: 'u001', userSig: 'sig', name: '张三' }) -XuqmSDK.setUserInfo(null) // 登出 +XuqmSDK.setUserInfo(null) // 登出,触发所有子 SDK 登出 ``` -### 日志(@xuqm/rn-log) +子 SDK 自动订阅 `setUserInfo` 事件:Push 自动注册/解绑 token,IM 自动登录/断开,Update 更新 userId。 + +## 日志 SDK(@xuqm/rn-log) + ```ts -import { XLog } from '@xuqm/rn-log' // 注意是 XLog 不是 Log +import { XLog } from '@xuqm/rn-log' XLog.setLogLevel('debug') XLog.setEnvironment('production') XLog.startCapture() // 开启全局错误捕获 XLog.event('page_view', { page: 'home' }) XLog.captureError(new Error('xxx')) +XLog.defineFunnel('checkout', ['cart_view', 'checkout_start', 'payment_done']) ``` `logApiUrl` 由 SDK 在 init 后从 `/api/sdk/config` 自动获取,无需 App 传入。 +## 关键约束 + +- `XuqmSDK.initialize()` 签名不得更改 +- `XuqmSDK.setUserInfo()` 现有字段不得删除 +- 新增字段一律为可选,有合理默认值 +- 不允许静默降级(autoInit 失败时 `__DEV__` 模式 throw) +- rn-common 与 rn-log 解耦:rn-common 可独立使用;rn-log peerDep rn-common + ## XWebView JSBridge 标准 Handler -见 `docs/XWebView-JSBridge.md`(已有文档,以该文档为准)。 - -## 任务文档 - -开发前阅读 `/Users/xuqinmin/Projects/XuqmProjects/YiwangxinApp4/docs/agent-tasks/` 中对应的 agent 任务文件: - -- Agent 2 任务:`agent2-rn-common.md` -- Agent 3 任务:`agent3-rn-update-xwebview.md` -- Agent 4 任务:`agent4-rn-log.md` -- Agent 8 任务:`agent8-docs.md` +见 `docs/XWebView-JSBridge.md`。 ## 常用命令 @@ -87,4 +92,8 @@ XLog.captureError(new Error('xxx')) yarn workspace @xuqm/rn-common typecheck yarn workspace @xuqm/rn-log typecheck yarn workspace @xuqm/rn-update typecheck +yarn workspace @xuqm/rn-push typecheck +yarn workspace @xuqm/rn-im typecheck +yarn workspace @xuqm/rn-xwebview typecheck +yarn workspace @xuqm/rn-license typecheck ``` diff --git a/docs/架构总览.md b/docs/架构总览.md new file mode 100644 index 0000000..e248505 --- /dev/null +++ b/docs/架构总览.md @@ -0,0 +1,105 @@ +# XuqmGroup RN SDK 架构总览 + +## 整体架构 + +``` +宿主 App(YiwangxinApp4 等) + └── 初始化(方式A: 配置文件自动 / 方式B: 手动 initialize) + ↓ + @xuqm/rn-common (核心层 v0.4.0) + ├── XuqmSDK.initialize() → 拉取 /api/sdk/config + ├── XuqmSDK.setUserInfo() → 通知所有子 SDK(订阅者模式) + ├── api/(useApi / usePageApi / apiRequest) + ├── device(设备信息 / 厂商检测) + ├── xwebview(JSBridge 桥接控制) + ├── ui/(toast / alert / confirm) + └── logApiUrl / logEnabled(从平台配置获取) + ↓ peerDep / 依赖 + 子 SDK(各自独立) + ├── @xuqm/rn-update → OTA 热更新 / APK 更新 + ├── @xuqm/rn-push → 厂商推送(peerDep: rn-common) + ├── @xuqm/rn-im → IM 会话 / 消息(peerDep: rn-common) + ├── @xuqm/rn-xwebview → WebView UI 组件(peerDep: rn-common) + ├── @xuqm/rn-license → 证书授权(peerDep: rn-common) + └── @xuqm/rn-log → 日志 / 追踪(peerDep: rn-common) +``` + +## 依赖关系 + +``` +rn-common (核心,无 SDK 内部依赖) + ↑ peerDep + ├── rn-update (直接依赖 rn-common) + ├── rn-push (peerDep rn-common) + ├── rn-im (peerDep rn-common + watermelondb) + ├── rn-xwebview (peerDep rn-common + webview + navigation) + ├── rn-license (peerDep rn-common + quick-crypto) + └── rn-log (peerDep rn-common) +``` + +## 用户状态分发机制 + +``` +XuqmSDK.setUserInfo(info) + ├── PushSDK: 检测厂商 → 获取厂商配置 → 注册 token + ├── ImSDK: 若 userSig 存在且 imEnabled → 自动登录 WebSocket + ├── UpdateSDK: 更新 userId(用于定向更新) + └── LicenseSDK: 更新用户上下文 + +XuqmSDK.setUserInfo(null) + ├── PushSDK: 解绑 token + ├── ImSDK: 断开 WebSocket + └── 其他子 SDK: 清理用户状态 +``` + +各子 SDK 在模块加载时通过 `_registerUserInfoHandler()` 注册处理器,无需 App 手动协调。 + +## 初始化流程 + +### 方式 A(自动) + +``` +Metro alias → @xuqm/autoinit-config → encrypted .xuqmconfig + ↓ import 时触发 +autoInit.ts → require('@xuqm/autoinit-config') + ↓ 解密(PBKDF2 + AES-256-GCM) +initWithConfigFile() → initialize({ appKey, platformUrl }) + ↓ HTTP GET +/api/sdk/config → 返回 { apiUrl, imWsUrl, fileServiceUrl, ... } +``` + +### 方式 B(手动) + +``` +App 代码调用 XuqmSDK.initialize({ appKey }) + ↓ HTTP GET +/api/sdk/config → 解析平台配置 + ↓ +各子 SDK 就绪(等待 setUserInfo 触发具体功能) +``` + +## 数据流 + +``` +App 代码 + ↓ 调用 API +子 SDK(update/push/im/license/log) + ↓ 使用 apiRequest() +rn-common http.ts + ↓ 附加 Bearer Token + 处理响应 +平台服务(tenant-service / im-service / push-service / update-service / log-service) +``` + +## 包发布 + +所有包发布到 Nexus npm 私有仓库:`https://nexus.xuqinmin.com/repository/npm-hosted/` + +```bash +# 发布单个包 +cd packages/common && npm publish + +# 发布所有包 +for pkg in common update push im xwebview license log; do + cd packages/$pkg && npm publish && cd ../.. +done +``` diff --git a/docs/模块待开发说明.md b/docs/模块待开发说明.md new file mode 100644 index 0000000..250cd76 --- /dev/null +++ b/docs/模块待开发说明.md @@ -0,0 +1,85 @@ +# 模块待开发说明 + +> 以下模块当前代码冻结,仅补充文档。后续开发需单独评估。 + +--- + +## rn-push(厂商推送) + +### 当前状态 + +- FCM/APNS 基础注册已实现 +- HMS/VIVO/OPPO 厂商推送集成框架已建 +- 自动厂商检测(通过原生模块 `NativePush`) +- Token 注册/解绑 API 完整 +- 离线推送开关、免打扰时间段 API 已实现 + +### 后续待做 + +- 厂商推送 token 注册完整流程(华为 HMS 需要 AppGallery Connect 配置) +- 设备绑定/解绑统一 API(当前分散在各厂商 SDK) +- 角标管理(badge count 同步) +- 厂商推送通道选择(高优先级 vs 普通通知) +- 推送数据分析(到达率、点击率统计) + +### 技术要点 + +- 原生模块 `XuqmPushModule` 需要在宿主 App 中链接 +- 各厂商 SDK 初始化需要对应的 App ID / App Secret +- `setUserInfo(info)` 触发自动注册,`setUserInfo(null)` 触发自动解绑 + +--- + +## rn-im(即时通讯) + +### 当前状态 + +- 腾讯云 IM SDK 封装(WebSocket/STOMP 传输) +- WatermelonDB 本地消息持久化 +- 基础会话/消息 API 完整(15 种消息类型) +- 群组管理 API 完整 +- 关系链(好友/黑名单)API 完整 +- 离线消息同步、消息搜索(本地) + +### 后续待做 + +- 消息已读回执(单聊已读状态同步) +- 群组消息已读回执统计 +- 离线消息补全(当前为 best-effort 同步) +- 消息撤回时间窗口控制(服务端策略) +- 语音消息录制组件(当前仅发送本地音频文件) +- 图片/视频消息缩略图生成 +- 消息转发、合并转发 UI +- @所有人(@all)支持 + +### 技术要点 + +- 需要 `@nozbe/watermelondb` 依赖 +- DB 名称由 `appKey + userId` 派生,多账号隔离 +- 断线重连由 `ImClient` 内部处理(指数退避) +- `setUserInfo` 自动触发 IM 登录(若 `imEnabled` 为 true) + +--- + +## rn-license(证书授权) + +### 当前状态 + +- 证书授权基础流程已实现 +- 设备 License 验证(`checkLicense`) +- License 状态查询(`getStatus`) +- 本地缓存(AsyncStorage) + +### 后续待做 + +- 证书到期提醒(服务端推送 + 本地定时检查) +- 证书自动刷新机制 +- 多设备 License 管理 +- 离线 License 验证(降级策略) +- License 使用量统计 + +### 技术要点 + +- 需要 `react-native-quick-crypto` 依赖 +- 内部自动等待 `XuqmSDK` 初始化完成 +- License 服务地址从平台配置获取(`licenseUrl`) diff --git a/packages/common/README.md b/packages/common/README.md new file mode 100644 index 0000000..540d8b9 --- /dev/null +++ b/packages/common/README.md @@ -0,0 +1,109 @@ +# @xuqm/rn-common + +XuqmGroup RN SDK 核心模块。负责 SDK 初始化、HTTP 请求、Token 管理、设备信息、XWebView Bridge、API hooks。 + +## 安装 + +```bash +yarn add @xuqm/rn-common @react-native-async-storage/async-storage +``` + +Peer dependencies:`react >= 18`,`react-native >= 0.76`,`axios >= 1.0.0` + +## 初始化 + +### 方式 A — 配置文件自动初始化(推荐) + +在宿主项目中配置 Babel alias `@xuqm/autoinit-config` → 加密配置文件: + +```js +// metro.config.js +config.resolver.extraNodeModules = { + '@xuqm/autoinit-config': './path/to/your.xuqmconfig', +}; +``` + +SDK 在 import 时自动调用 `initWithConfigFile()`,App 无需任何 init 代码。 + +### 方式 B — 手动初始化 + +```ts +import { XuqmSDK } from '@xuqm/rn-common' + +await XuqmSDK.initialize({ appKey: 'xxx' }) // 公有平台 +await XuqmSDK.initialize({ appKey: 'xxx', platformUrl: 'https://xxx' }) // 私有化 +await XuqmSDK.awaitInitialization() // 等待配置拉取 +``` + +## API + +### 初始化 + +| API | 说明 | +|-----|------| +| `XuqmSDK.initialize(opts)` | 初始化,拉取平台配置 | +| `XuqmSDK.awaitInitialization()` | 等待异步初始化完成 | +| `isInitialized()` | 检查 SDK 是否就绪 | +| `getConfig()` | 获取已解析的平台配置 | + +### 用户信息 + +| API | 说明 | +|-----|------| +| `XuqmSDK.setUserInfo(info)` | 设置用户信息,触发所有子 SDK 登录 | +| `XuqmSDK.setUserInfo(null)` | 登出,触发全局登出 | +| `getUserId()` | 获取当前 userId | +| `getUserInfo()` | 获取当前用户信息 | +| `setUserId(id)` | 直接设置 userId(内部用) | + +### HTTP + +| API | 说明 | +|-----|------| +| `apiRequest(url, options?)` | 带 Bearer token 的 HTTP 请求 | +| `configureHttp(opts)` | 覆盖 HTTP 基础配置 | +| `_getToken()` | 获取当前 token(内部用) | +| `_saveToken(token)` | 保存 token(内部用) | +| `_clearToken()` | 清除 token(内部用) | + +### 设备 + +| API | 说明 | +|-----|------| +| `getDeviceId()` | 获取设备稳定 UUID | +| `getDeviceInfo()` | 获取设备品牌/型号/系统信息 | +| `detectPushVendor()` | 检测设备推送厂商 | + +### XWebView Bridge + +| API | 说明 | +|-----|------| +| `openXWebView(url, options?)` | 打开 WebView | +| `setXWebViewController(controller)` | 设置全局 WebView 控制器 | +| `getXWebViewConfig()` | 获取当前 WebView 配置 | +| `XWebViewControl` | WebView 控制器类 | + +### API Hooks + +| API | 说明 | +|-----|------| +| `useApi(method, url, opts?)` | React hook:带 loading/error 的 API 调用 | +| `usePageApi(method, url, opts?)` | React hook:分页 API | +| `useRequest(opts?)` | React hook:通用请求 | +| `setGlobalApiErrorHandler(handler)` | 设置全局 API 错误处理 | + +### UI + +| API | 说明 | +|-----|------| +| `showToast(msg)` | 显示 toast | +| `showAlert(title, msg)` | 显示 alert | +| `showConfirm(title, msg)` | 显示确认对话框 | +| `ScaledImage` | 等比缩放图片组件 | + +### 常量 + +| 常量 | 说明 | +|------|------| +| `DEFAULT_TENANT_PLATFORM_URL` | 默认公有平台地址 | +| `DEFAULT_IM_WS_URL` | 默认 IM WebSocket 地址 | diff --git a/packages/im/README.md b/packages/im/README.md new file mode 100644 index 0000000..fe02815 --- /dev/null +++ b/packages/im/README.md @@ -0,0 +1,127 @@ +# @xuqm/rn-im + +XuqmGroup RN SDK IM 模块。提供 WebSocket 实时通信、会话管理、消息收发、群组管理、关系链管理。 + +## 安装 + +```bash +yarn add @xuqm/rn-im @nozbe/watermelondb +``` + +Peer dependencies:`@nozbe/watermelondb >= 0.27.0`,`react-native >= 0.76` + +## 使用 + +**无需手动初始化。** `XuqmSDK.setUserInfo({ userId, userSig })` 被调用时,SDK 自动完成 IM 登录。 + +```ts +import { ImSDK } from '@xuqm/rn-im' + +// 发送消息 +await ImSDK.sendTextMessage('user_002', 'SINGLE', 'Hello!') + +// 获取会话列表 +const conversations = await ImSDK.listConversations() + +// 监听新消息 +ImSDK.addListener({ + onMessage: (msg) => console.log('新消息:', msg), + onConnected: () => console.log('IM 已连接'), +}) +``` + +## API + +### 连接管理 + +| API | 说明 | +|-----|------| +| `ImSDK.login(userId, userSig)` | 手动登录(通常不需要直接调用) | +| `ImSDK.refreshToken(userSig)` | 刷新 userSig(过期时调用) | +| `ImSDK.disconnect()` | 断开连接 | +| `ImSDK.reconnect()` | 重新连接 | +| `ImSDK.isConnected()` | 检查连接状态 | + +### 消息 API + +| API | 说明 | +|-----|------| +| `ImSDK.sendMessage(toId, chatType, msgType, content, mentionedUserIds?)` | 发送消息 | +| `ImSDK.sendTextMessage(toId, chatType, content, mentionedUserIds?)` | 发送文本 | +| `ImSDK.sendImageMessage(toId, chatType, localUri, width?, height?)` | 发送图片 | +| `ImSDK.sendVideoMessage(toId, chatType, localUri, thumbnailUri?, duration?)` | 发送视频 | +| `ImSDK.sendAudioMessage(toId, chatType, localUri, duration)` | 发送语音 | +| `ImSDK.sendFileMessage(toId, chatType, localUri, filename, size)` | 发送文件 | +| `ImSDK.sendQuoteMessage(toId, chatType, quotedMsgId, quotedContent, text)` | 引用回复 | +| `ImSDK.sendLocationMessage(toId, chatType, latitude, longitude, title?, address?)` | 发送位置 | +| `ImSDK.revokeMessage(messageId)` | 撤回消息 | +| `ImSDK.editMessage(messageId, content)` | 编辑消息 | + +### 会话 API + +| API | 说明 | +|-----|------| +| `ImSDK.listConversations()` | 获取会话列表 | +| `ImSDK.subscribeConversations(callback)` | 订阅会话变更(返回 unsubscribe) | +| `ImSDK.markRead(targetId, chatType?)` | 标记已读 | +| `ImSDK.deleteConversation(targetId, chatType)` | 删除会话 | +| `ImSDK.getTotalUnreadCount()` | 获取总未读数 | + +### 历史消息 + +| API | 说明 | +|-----|------| +| `ImSDK.fetchHistory(toId, page?, size?)` | 获取单聊历史(优先本地缓存) | +| `ImSDK.fetchGroupHistory(groupId, page?, size?)` | 获取群聊历史 | +| `ImSDK.fetchHistoryWithFilters(toId, params)` | 按条件查询历史 | +| `ImSDK.searchMessages(params)` | 搜索消息(本地 WatermelonDB) | +| `ImSDK.locateHistoryPage(toId, messageId)` | 定位消息所在页 | + +### 群组 API + +| API | 说明 | +|-----|------| +| `ImSDK.createGroup(name, memberIds, groupType?)` | 创建群组 | +| `ImSDK.listGroups()` | 获取群组列表 | +| `ImSDK.getGroupInfo(groupId)` | 获取群组信息 | +| `ImSDK.listGroupMembers(groupId)` | 获取群成员 | +| `ImSDK.addGroupMember(groupId, userId)` | 添加成员 | +| `ImSDK.removeGroupMember(groupId, userId)` | 移除成员 | +| `ImSDK.setGroupRole(groupId, userId, role)` | 设置管理员 | +| `ImSDK.muteGroupMember(groupId, userId, minutes)` | 禁言 | +| `ImSDK.dismissGroup(groupId)` | 解散群组 | + +### 关系链 API + +| API | 说明 | +|-----|------| +| `ImSDK.listFriends()` | 好友列表 | +| `ImSDK.addFriend(friendId)` | 添加好友 | +| `ImSDK.removeFriend(friendId)` | 删除好友 | +| `ImSDK.sendFriendRequest(toUserId, remark?)` | 发送好友申请 | +| `ImSDK.acceptFriendRequest(requestId)` | 接受好友申请 | +| `ImSDK.addToBlacklist(blockedUserId)` | 加入黑名单 | +| `ImSDK.checkBlacklist(targetUserId)` | 检查黑名单 | + +### 事件监听 + +```ts +ImSDK.addListener({ + onConnected: () => {}, + onDisconnected: (code, reason) => {}, + onMessage: (msg) => {}, + onGroupMessage: (msg) => {}, + onSystemMessage: (msg) => {}, + onRead: (msg) => {}, + onRevoke: (data) => {}, +}) +ImSDK.removeListener(listener) +``` + +## 消息类型 + +`TEXT` / `IMAGE` / `VIDEO` / `AUDIO` / `FILE` / `CUSTOM` / `LOCATION` / `NOTIFY` / `RICH_TEXT` / `CALL_AUDIO` / `CALL_VIDEO` / `QUOTE` / `MERGE` / `REVOKED` / `FORWARD` + +## 本地存储 + +使用 WatermelonDB 进行本地消息缓存,支持离线消息同步和本地搜索。DB 名称由 `appKey + userId` 派生。 diff --git a/packages/license/README.md b/packages/license/README.md new file mode 100644 index 0000000..f28c231 --- /dev/null +++ b/packages/license/README.md @@ -0,0 +1,59 @@ +# @xuqm/rn-license + +XuqmGroup RN SDK 证书授权模块。提供设备 License 验证能力。 + +## 安装 + +```bash +yarn add @xuqm/rn-license react-native-quick-crypto +``` + +Peer dependencies:`react-native >= 0.76`,`@react-native-async-storage/async-storage >= 1.21.0`,`react-native-quick-crypto >= 0.7.0` + +## 使用 + +**无需独立初始化。** 内部自动调用 `awaitInitialization()` 等待 `XuqmSDK` 就绪。 + +```ts +import { checkLicense, getStatus, clear } from '@xuqm/rn-license' + +// 验证 License +const result = await checkLicense() +if (result.type === 'success') { + console.log('License 通过:', result.reason) +} else { + console.error('License 失败:', result.message) +} + +// 获取状态 +const status = await getStatus() // 'ok' | 'denied' | 'unknown' + +// 清除本地缓存(调试用) +await clear() +``` + +## API + +| API | 说明 | +|-----|------| +| `checkLicense(userInfo?)` | 验证设备 License | +| `getStatus()` | 获取当前 License 状态 | +| `getDeviceId()` | 获取设备 ID | +| `clear()` | 清除本地 License 缓存 | + +## 类型 + +```ts +interface LicenseUserInfo { + userId?: string + userSig?: string +} + +type LicenseStatus = 'ok' | 'denied' | 'unknown' + +interface LicenseResult { + type: 'success' | 'denied' | 'error' + reason?: string + message?: string +} +``` diff --git a/packages/log/README.md b/packages/log/README.md new file mode 100644 index 0000000..be68d59 --- /dev/null +++ b/packages/log/README.md @@ -0,0 +1,89 @@ +# @xuqm/rn-log + +XuqmGroup RN SDK 日志模块。提供日志采集、错误追踪、漏斗分析能力。 + +## 安装 + +```bash +yarn add @xuqm/rn-log +``` + +Peer dependencies:`@xuqm/rn-common >= 0.4.0`,`@react-native-async-storage/async-storage >= 1.21.0`,`react-native >= 0.76` + +## 快速开始 + +```ts +import { XLog } from '@xuqm/rn-log' + +// 1. 设置日志级别 +XLog.setLogLevel('debug') // 'debug' | 'info' | 'warn' | 'error' + +// 2. 设置环境标签 +XLog.setEnvironment('production') // 'development' | 'staging' | 'production' + +// 3. 开启全局错误捕获(App 启动时调用一次) +XLog.startCapture() + +// 4. 记录自定义事件 +XLog.event('page_view', { page: 'home' }) + +// 5. 上报错误 +try { ... } catch (e) { XLog.captureError(e) } +``` + +## API + +### XLog 对象 + +| API | 说明 | +|-----|------| +| `XLog.setLogLevel(level)` | 设置最低日志级别(低于此级别的事件被丢弃) | +| `XLog.setEnvironment(env)` | 设置环境标签(附加到每个事件) | +| `XLog.startCapture()` | 开启全局 JS 错误和未处理 Promise rejection 捕获 | +| `XLog.event(name, properties?)` | 记录自定义分析事件 | +| `XLog.captureError(error, metadata?)` | 上报 JS 异常 | +| `XLog.warn(message, metadata?)` | 记录警告(需级别允许) | +| `XLog.info(message, metadata?)` | 记录信息事件(需级别允许) | +| `XLog.defineFunnel(id, steps)` | 定义漏斗(按步骤顺序追踪转化) | +| `XLog.getFunnelProgress(funnelId)` | 获取客户端漏斗进度 | +| `XLog.flush()` | 立即刷新所有待发送事件(如 App 进后台前调用) | + +### 事件类型 + +| 类型 | 说明 | +|------|------| +| `LogEvent` | 自定义事件(`type: 'event'`) | +| `IssueEvent` | 错误事件(`type: 'js_error'` / `'native_crash'` / `'api_error'` / `'warning'`) | + +### 日志级别 + +`'debug'` (0) < `'info'` (1) < `'warn'` (2) < `'error'` (3) + +### 漏斗分析示例 + +```ts +import { XLog } from '@xuqm/rn-log' + +// 定义漏斗 +XLog.defineFunnel('checkout', ['cart_view', 'checkout_start', 'payment_done']) + +// 在业务代码中记录步骤事件(SDK 自动推进漏斗) +XLog.event('cart_view') +XLog.event('checkout_start') +XLog.event('payment_done') + +// 查询进度 +const progress = XLog.getFunnelProgress('checkout') +console.log(progress?.completedSteps) // ['cart_view', 'checkout_start', 'payment_done'] +``` + +## 工作原理 + +1. **LogQueue**:事件先进入内存队列,按批次异步上传到 `logApiUrl` +2. **ErrorCapture**:`startCapture()` 注册全局 `ErrorUtils` handler,自动捕获未处理异常 +3. **Fingerprint**:为每个错误生成指纹(基于 message + stack),用于服务端去重聚合 +4. **FunnelTracker**:客户端维护漏斗进度,服务端跨 session 聚合 + +## 配置 + +`logApiUrl` 和 `logEnabled` 由 `@xuqm/rn-common` 在 init 后从 `/api/sdk/config` 自动获取,无需 App 传入。 diff --git a/packages/push/README.md b/packages/push/README.md new file mode 100644 index 0000000..d9106bf --- /dev/null +++ b/packages/push/README.md @@ -0,0 +1,53 @@ +# @xuqm/rn-push + +XuqmGroup RN SDK 推送模块。自动检测设备厂商并完成推送 token 注册。 + +## 安装 + +```bash +yarn add @xuqm/rn-push +``` + +Peer dependencies:`react-native >= 0.76` + +## 使用 + +**无需手动初始化。** `XuqmSDK.setUserInfo(info)` 被调用时,SDK 自动完成: +1. 检测设备厂商(华为/小米/OPPO/vivo/荣耀/FCM/APNs) +2. 从平台获取该厂商推送配置 +3. 调用厂商 SDK 完成设备注册 +4. 收到 token 后自动上报绑定到平台 + +```ts +import { PushSDK } from '@xuqm/rn-push' + +// 通常不需要手动调用,setUserInfo 会自动触发 +// 设置离线推送开关 +await PushSDK.setOfflinePushEnabled(false) + +// 设置免打扰时间段(24 小时制) +await PushSDK.setQuietHours('22:00', '08:00') + +// 清除免打扰 +await PushSDK.clearQuietHours() + +// 主动登出(通常不需要,setUserInfo(null) 自动触发) +await PushSDK.logout() +``` + +## API + +| API | 说明 | +|-----|------| +| `PushSDK.setOfflinePushEnabled(enabled)` | 设置离线推送开关 | +| `PushSDK.setQuietHours(start, end)` | 设置免打扰时间段 | +| `PushSDK.clearQuietHours()` | 清除免打扰设置 | +| `PushSDK.logout()` | 解绑推送 token | + +## 支持厂商 + +HUAWEI / XIAOMI / OPPO / VIVO / HONOR / FCM / APNS + +## 依赖 + +需要原生模块 `NativeModules.XuqmPushModule` 已链接到宿主 App。 diff --git a/packages/update/README.md b/packages/update/README.md new file mode 100644 index 0000000..1b5c661 --- /dev/null +++ b/packages/update/README.md @@ -0,0 +1,116 @@ +# @xuqm/rn-update + +XuqmGroup RN SDK 更新模块。提供 App 整包更新检查和 RN 插件热更新能力。 + +## 安装 + +```bash +yarn add @xuqm/rn-update @react-native-async-storage/async-storage +``` + +Peer dependencies:`react-native >= 0.76`,`@react-native-async-storage/async-storage >= 1.21.0` + +## 快速开始 + +```ts +import { UpdateSDK } from '@xuqm/rn-update' + +// 注册插件 +UpdateSDK.registerPlugins([ + { moduleId: 'szyx' }, + { moduleId: 'miniapp' }, +]) + +// 注入宿主 BundleRuntime +UpdateSDK.setBundleCallbacks({ + writeBundle: writeBundleFile, + reloadBundle: loadBundle, +}) + +// 检查 App 更新 +const info = await UpdateSDK.checkAppUpdate() +if (info.needsUpdate) { + await UpdateSDK.downloadAndInstallApk(info.downloadUrl) +} + +// 检查插件更新 +const pluginInfo = await UpdateSDK.checkPluginUpdate('szyx') +if (pluginInfo.needsUpdate) { + await UpdateSDK.updatePlugin('szyx', { onProgress: setProgress }) +} +``` + +## API + +### 插件注册 + +| API | 说明 | +|-----|------| +| `UpdateSDK.registerPlugins(plugins)` | 批量注册插件 | +| `UpdateSDK.registerPlugin(meta)` | 单个注册(向后兼容) | +| `UpdateSDK.setBundleCallbacks(callbacks)` | 注入宿主写入/重载能力 | +| `UpdateSDK.getRegisteredPlugins()` | 获取已注册插件列表 | + +### App 整包更新 + +| API | 说明 | +|-----|------| +| `UpdateSDK.checkAppUpdate(bypassIgnore?)` | 检查 App 更新(30 分钟缓存) | +| `UpdateSDK.downloadApk(info, options?)` | 下载 APK,返回 ArrayBuffer | +| `UpdateSDK.downloadAndInstallApk(url, options?)` | 下载 APK 并调起系统安装器(Android) | +| `UpdateSDK.openStore(appStoreUrl?, marketUrl?)` | 跳转应用商店 | +| `UpdateSDK.getAppVersionCode()` | 获取当前 versionCode | +| `UpdateSDK.getAppVersionName()` | 获取当前 versionName | + +### 插件热更新 + +| API | 说明 | +|-----|------| +| `UpdateSDK.checkPluginUpdate(moduleId)` | 检查插件更新(30 分钟缓存) | +| `UpdateSDK.updatePlugin(moduleId, options?)` | 一步完成:检查 → 下载 → 写文件 → 重载 | + +### 类型定义 + +```ts +interface PluginRegistration { + moduleId: string +} + +interface AppUpdateInfo { + needsUpdate: boolean + versionName?: string + versionCode?: number + downloadUrl?: string + changeLog?: string + forceUpdate?: boolean + appStoreUrl?: string + marketUrl?: string + apkHash?: string | null +} + +interface PluginUpdateInfo { + needsUpdate: boolean + latestVersion: string + currentVersion: string + downloadUrl: string + md5: string + minCommonVersion: string + note: string + forceUpdate?: boolean +} + +interface CachedRnBundle { + moduleId: string + version: string + md5: string + downloadedAt: string + source: string +} +``` + +## 工作原理 + +- 版本号由 SDK 自动从本地 AsyncStorage 缓存读取,首次为 `0.0.0` +- 更新检查结果缓存 30 分钟(TTL),避免频繁请求 +- `updatePlugin()` 内部自动完成:`checkPluginUpdate` → `fetch(downloadUrl)` → AsyncStorage 缓存 → `writeBundle` → `reloadBundle` +- `silent: true` 时跳过 `reloadBundle`,下次启动生效 diff --git a/packages/xwebview/README.md b/packages/xwebview/README.md new file mode 100644 index 0000000..f9e47a1 --- /dev/null +++ b/packages/xwebview/README.md @@ -0,0 +1,78 @@ +# @xuqm/rn-xwebview + +XuqmGroup RN SDK WebView 模块。提供增强型 WebView 组件和 JSBridge 通信能力。 + +## 安装 + +```bash +yarn add @xuqm/rn-xwebview react-native-webview react-native-blob-util react-native-svg +``` + +Peer dependencies:`react >= 18`,`react-native >= 0.76`,`@react-navigation/native >= 7.0.0` + +## 组件 + +### XWebViewScreen — 全屏 WebView(React Navigation screen) + +```tsx +import { XWebViewScreen } from '@xuqm/rn-xwebview' + +// 在导航中使用 + +// URL 通过 openXWebView() 预设 +``` + +### XWebViewView — 嵌入式 WebView 组件 + +```tsx +import { XWebViewView } from '@xuqm/rn-xwebview' + + +``` + +### XWebViewProgress — 进度条 + +```tsx +import { XWebViewProgress } from '@xuqm/rn-xwebview' + + +``` + +## API + +| API | 说明 | +|-----|------| +| `openXWebView(url, options?)` | 打开 WebView 页面 | +| `setXWebViewController(controller)` | 设置全局 WebView 控制器 | +| `getXWebViewConfig()` | 获取当前 WebView 配置 | +| `XWebViewControl` | WebView 控制器类 | + +## XWebViewConfig 参数 + +| 参数 | 类型 | 说明 | +|------|------|------| +| `url` | string | 初始加载地址 | +| `title` | string | 页面标题 | +| `jsBridgeName` | string | JS 桥接对象名(默认 `'XWebViewBridge'`) | +| `hideToolbar` | boolean | 隐藏顶栏 | +| `userAgent` | string? | 自定义 User-Agent | +| `injectedJavaScript` | string? | 页面加载后注入的 JS | +| `onMessage` | (event) => void | H5 发送消息回调 | + +## JSBridge 通信 + +```js +// H5 → Native +window.XWebViewBridge.postMessage(JSON.stringify({ type: 'login', token: '...' })) + +// Native → H5 +controller.postMessageToWeb("window.dispatchEvent(new CustomEvent('nativeMsg', { detail: { key: 'value' } }))") +``` + +详细 handler 列表见 `@xuqm/rn-common` 的 `xwebview/` 目录和 `docs/XWebView-JSBridge.md`。