XuqmGroup-Server/xuqm-bugcollect-service/docs/BugCollect-API-v1-Review.md

286 行
14 KiB
Markdown

# BugCollect API v1 规范审阅报告
> **审阅对象**`docs/BugCollect-API-v1.md`(版本 1.1.0
> **涉及范围**:移动端 SDK、租户平台、服务端
> **审阅日期**2026-06-17
> **审阅结论**接口契约整体已比较完整,但与当前代码实现、SDK 客户端、租户平台存在较多不一致,且有多个端点/能力“文档已定义、服务端未实现”。
---
## 一、文档与实现不一致(优先级最高)
这些不匹配会导致 SDK、租户平台、服务端三方联调失败或语义错误。
### 1. Session 上报端点未实现
- **文档**`4.4` 定义了 `POST /bugcollect/v1/sessions/batch`,并声明用于计算 `crashFreeSessionRate`
- **实现**`LogController.java:31-43` 只有 `/issues/batch`、`/events/batch`,**没有 `/sessions/batch`**。
- **建议**
- 要么服务端补齐 Session 接收、聚合、Crash-Free 计算(推荐);
- 要么文档中先标注「待定/未实现」,避免 SDK 团队按文档开发。
### 2. 服务端 Webhook 实现与文档严重不符
- **事件类型 mismatch**
- 文档 `8.2/8.3` 定义 `events: ["issue.created", "issue.fatal", ...]`
- 实现 `WebhookService.java:52``issue.getType()`(实际是 `level``fatal`/`error`)去匹配事件列表,且永远发送 `event: "issue.new"``WebhookService.java:71`)。
- **签名校验缺失**:文档 `8` 声称回调带 `X-BugCollect-Signature: sha256=<hmac>`,实现里完全没有签名。
- **字段 mismatch**:文档请求体有 `secret`,实现 `WebhookRequest.java` 根本没有 `secret` 字段,反而多了文档里没提到的 `cooldownSec`
- **建议**
- 统一 Webhook 模型:按文档实现 `secret`、`events`、签名;
- 事件触发逻辑改为按业务事件(`issue.created`/`fatal`/`threshold`/`regressed`/`resolved`)匹配;
- 如果 `cooldownSec` 需要保留,也写进文档。
### 3. Issue 详情 / IssueEvent 响应字段结构不一致
- **文档 `6.3`**`exception` 是对象 `{type, value, stacktrace, stackSymbolicated}`,`breadcrumbs`/`tags`/`device` 是 JSON 对象。
- **实现 `IssueEventResponse.java`**:字段被拍平为 `exceptionType`、`exceptionValue`、`message`、`stack`、`stackSymbolicated`,且 `breadcrumbs`/`tags`/`device` 是 **String**(数据库 JSON 字符串直接返回)。
- **影响**:租户平台若按文档解析会拿到字符串而不是对象。
- **建议**
- 服务端在 `toIssueEventResponse` 里把 JSON 字符串反序列化为对象返回;
- 或者调整文档以匹配实现;但前者更符合 Sentry 对齐原则。
### 4. IssueResponse 仍返回已删除字段
- **文档 `13` Breaking Change**`isResolved` 已移除,改用 `status`
- **实现 `IssueResponse.java:18`** 仍然包含 `isResolved`,且 `events` 字段虽然总是 `null`,却还存在。
- **建议**:清理 DTO,移除 `isResolved``events`(如需兼容可加 `@JsonIgnore` 过渡)。
### 5. 分配责任人端点参数形式不一致
- **文档 `7.3`**`PUT /issues/{id}/assign`,请求体 JSON `{ "assignee": "user@example.com" }`
- **实现 `LogController.java:106-110`**:使用 `@RequestParam String assignee`,即 `?assignee=xxx`
- **建议**:按文档改为 `@RequestBody` 接收 JSON。
### 6. 批量操作缺少 `appKey` 说明
- **文档 `7.4`**:请求体只有 `{ids, action}`
- **实现 `LogController.java:114-120`**:额外要求 `@RequestParam String appKey`
- **建议**:文档补全 `appKey` 必填参数,并说明用于权限校验。
### 7. SourceMap 上传对多平台处理简陋
- **文档 `4.3`**Android 上传 `mapping.txt`,iOS 上传 `dSYM.zip`,RN 上传 `.map`
- **实现 `SourcemapService.java:43`****所有平台都保存为 `index.map`**。iOS 的 zip 和 Android 的 txt 会被重命名为 `.map`,后续符号化无法正确识别。
- **建议**
-`platform` 决定文件扩展名和解析方式;
- Android `mapping.txt` 保留文本;
- iOS `dSYM.zip` 解压后按 UUID/架构索引;
- RN 按 `bundleName` + `appVersion` + 增加 `bundleVersion`/`codePushVersion` 维度存储。
### 8. 错误码表与代码/变更日志不一致
- **文档 `9`** 没有 `40006`(缺失 `eventId`),但 **变更日志**提到 `40006`
- **实现 `IssueBatchRequest.java:20`**`eventId` 未加 `@NotBlank`,所以缺失时不会被 `40006` 拒绝。
- **建议**
- 文档补齐 `40006`
- 如果 `eventId` 是必填项,给 DTO 加 `@NotBlank` 或业务校验。
---
## 二、服务端实现可升级项
### 9. `affectedUsers` 统计逻辑错误
`LogService.java:84`
```java
if (userId != null) issue.setAffectedUsers(issue.getAffectedUsers() + 1);
```
这是“有 userId 就 +1”,同一个用户多次触发会重复累加,不是“受影响独立用户数”。
- **建议**:维护 `log_issue_users` 关联表或 HyperLogLog,按 `appKey + fingerprint + userId` 去重计算。
### 10. 符号化只是空壳
`LogService.java:139-142``triggerSymbolicationAsync` 只打印 debug,没有真正的
- ProGuard/R8 mapping 还原;
- dSYM 解析;
- SourceMap 解析。
- **建议**:补充符号化 Worker,或文档中标注该能力尚未可用;对移动 SDK 来说,符号化是核心卖点,不能仅停留在接口层面。
### 11. 概览统计 `crashFreeSessionRate` 未实现
`OverviewResponse` 里没有 `crashFreeSessionRate`,`LogService.getOverview` 也没有相关计算。
- **建议**:补齐 Session 表和计算逻辑,否则文档 `6.6` 中的字段是空的。
### 12. `log_sessions` 表未创建
变更日志说 v1.1 新增 `log_sessions`,但 `V3__bugcollect_v11_upgrade.sql` 里**没有 `CREATE TABLE log_sessions`**。
- **建议**:补 Flyway 迁移,并同步更新文档 `12` 的 Schema 章节。
### 13. 时间解析容错差
`LogService.parseDate` 直接把 `YYYY-MM-DD` 当 UTC/本地起始时间,且 `to` 默认加一天。对跨时区租户不友好。
- **建议**:明确文档中 `from/to` 是租户本地时区还是 UTC;服务端统一用 UTC 存储、按租户时区展示。
### 14. 漏斗分析未处理事件顺序
当前漏斗按 `sessionId` 集合去重,不检查步骤是否按顺序发生,也不支持时间窗口内必须完成。
- **建议**:文档明确漏斗语义(是否必须有序、是否允许跳步、时间窗口),实现按 `sessionId + timestamp` 排序校验。
### 15. 查询端点缺少 `environment` 筛选
文档 `6.1` Issue 列表没有 `environment` 参数,但 SDK 上报会区分 `production`/`staging`/`development`/`integration`。
- **建议**Issue/Event 列表都增加 `environment` 筛选,方便隔离开发噪声。
### 16. Issue 列表缺少 `assignee` 筛选
管理端常见需求是“我负责的 Issue”。
- **建议**`GET /issues` 增加 `assignee` 参数。
---
## 三、移动端 SDK 优化
### 17. React Native SDK 与文档能力不对齐
`BugCollect.ts` 当前缺失:
- `addBreadcrumb` API文档 `11.2` 示例有);
- `captureCrash` / fatal 级别上报;
- `device` 详细字段(仅 `model`/`osName`/`osVersion`);
- `breadcrumbs` 未附加到 IssueEvent;
- 平台字段只返回 `ios`/`android`,文档还有 `harmonyos`/`web`/`react-native`。
- **建议**RN SDK 补齐这些能力,版本号建议与文档/服务端对齐(目前是 `0.2.0`,文档示例是 `1.0.0`)。
### 18. Android SDK `buildDeviceInfo` 模拟器判断可加强
`BugCollect.kt:174-177` 只判断了部分模拟器特征,MiUI 云手机、第三方模拟器可能漏判。
- **建议**:增加 `Build.HARDWARE`、`Build.PRODUCT` 等特征,或允许租户平台配置过滤模拟器数据。
### 19. SDK 队列策略文档与实现不一致
- **文档 `11.4`**`fatal`/`error` 持久化补发,`warning` 及以下仅内存。
- **RN 实现 `LogQueue.ts`**:所有事件都写 `AsyncStorage`,重试 3 次后丢弃,没有区分级别持久化。
- **建议**:对齐策略,避免低级别事件撑满存储。
### 20. `LogQueue.ts` 上报路径缺少面包屑和设备信息
RN SDK 的 `IssueEvent` 没有 `breadcrumbs`、`device`,也没有 `exception` 对象里的完整内容。
- **建议**:补齐字段,否则服务端 Issue 详情会缺失上下文。
### 21. 缺少 iOS / HarmonyOS / Flutter / Web SDK
目前只有 Android 和 RN 有实现,其他平台文档里已列出但无代码。
- **建议**:文档中标注各平台 SDK 实现状态,避免误导集成方。
---
## 四、租户平台优化
### 22. TypeScript 类型与 v1.1 字段不同步
`tenant-platform/src/api/bugcollect.ts`
- 仍使用 `type`(应改为 `level`);
- 仍使用 `isResolved`(应改为 `status`);
- 仍使用 `appVersion`(应改为 `release`);
- `BugCollectEventItem` 仍用 `message`/`stack`/`metadata`(应改为 `exception.value`/`exception.stacktrace`/`tags`)。
- **建议**:按 v1.1 文档统一调整前端类型和字段映射。
### 23. Webhook 前端模型与服务端不匹配
前端 `BugCollectWebhook``eventTypes`、`cooldownSeconds`、`enabled`,但文档和服务端不完全一致。
- **建议**:先确定最终模型,再同步三端。
### 24. 缺少 Issue 管理操作的前端封装
`bugcollect.ts` 没有 `resolveIssue`/`ignoreIssue`/`assignIssue`/`bulk` 的 API 方法。
- **建议**:补齐管理端点调用。
### 25. 概览页类型缺少字段
`BugCollectOverview` 缺少 `openIssues`、`crashFreeSessionRate` 等文档字段。
- **建议**:与文档/服务端 DTO 对齐。
---
## 五、数据模型与 Schema
### 26. `fingerprint` 长度校验
文档说 fingerprint 是 64 字符 SHA-256 十六进制,但实现没有校验格式。
- **建议**DTO 加正则校验,或放宽文档描述(如果允许自定义 fingerprint
### 27. `log_events.properties` JSON 查询性能
漏斗、Event 查询直接查 JSON 字段,数据量大时会成为瓶颈。
- **建议**:对高频漏斗步骤考虑物化表或 `properties` 中关键字段单独建列。
### 28. SourceMap 缺少构建唯一标识
RN 热更新场景下,同一个 `appVersion` 可能对应多个 bundle 版本。
- **建议**SourceMap 上传增加 `bundleVersion` / `codePushVersion` / `buildNumber` 字段,否则堆栈无法定位到正确 bundle。
### 29. 索引建议
- `log_issue_events` 已按 `issue_id, created_at` 建索引,但如果按 `eventId` 去重频繁,建议把 `uk_app_event_id` 中的 `event_id` 改为 `NOT NULL`
- `log_events` 同理。
---
## 六、安全与合规
### 30. `appKey` 在上报体中双重携带
请求体里每个 event 都带 `appKey`,信封层没带;同时 RN SDK 还在 header 里带 `X-App-Key`
- **建议**:明确上报认证方式。如果按文档“只通过请求体 `appKey` + 服务开通状态鉴权”,可去掉 header 冗余;若要走 header,则文档也要说明。
### 31. Webhook `secret` 未持久化
文档要求配置 `secret` 用于签名,但数据库表、DTO、Service 都没有该字段。
- **建议**:数据库加 `secret` 列,并加密存储(至少不要明文落库)。
### 32. SourceMap 文件上传缺少大小/类型限制
实现直接写入磁盘,没有限制文件大小、校验 MIME/扩展名。
- **建议**:增加大小限制(如 200MB、平台对应扩展名校验,防止恶意上传。
### 33. 查询端点未校验 `appKey` 归属
文档 `3` 提到“查询参数中的 `appKey` 必须与鉴权租户匹配”,但 `LogController` 里没有这层校验(只是简单接收 appKey
- **建议**:在网关层或 Controller 层校验租户是否有权访问该 `appKey`
---
## 七、文档本身的可读性与一致性
### 34. 状态枚举冲突
- `5.5.4 Issue Status` 只有 `open/resolved/ignored`
- `7.4 bulk action``reopen`,对应状态 `open`
- 建议文档说明 `reopen` 不是持久状态,而是把状态重置为 `open`
### 35. `6.5 Event 列表` 响应类型错误
标题是 Event 列表,但返回示例写的是 `IssueEventResponse`。实际 `LogService.queryEvents` 返回的是自定义 analytics event。
- **建议**:文档增加 `LogEventResponse` 模型,字段为 `name`、`properties`、`sessionId` 等。
### 36. `Level` 与 SDK 方法映射不一致
文档 `5.5.1``warning` 对应 `warn()`,但数据模型字段应统一为 `warning`。RN SDK 的 `LogLevel` 类型却是 `'warn'`
- **建议**文档、SDK、服务端统一用 `warning` 作为数据字段,SDK 方法名可以叫 `warn()`
### 37. 缺少 OpenAPI / Swagger 声明
当前是纯 Markdown,SDK 和后端维护成本高。
- **建议**:补充 OpenAPI 3.0 YAML,并用 codegen 生成前端/后端 DTO,减少字段 mismatch。
---
## 建议的优先级排序
| 优先级 | 事项 |
|--------|------|
| **P0** | 补齐 `/sessions/batch` 或文档标注未实现;修复 Webhook 实现与文档严重不符;创建 `log_sessions` 表;修复 `affectedUsers` 统计 |
| **P1** | 统一 IssueEvent 响应结构JSON 对象 vs 字符串);清理 `IssueResponse` 遗留字段;修复 assign/bulk 参数形式;SourceMap 多平台处理 |
| **P2** | RN SDK 补齐 breadcrumbs/fatal/device;租户平台 TypeScript 类型同步;增加 `environment`/`assignee` 筛选 |
| **P3** | 符号化 Worker、漏斗语义增强、OpenAPI 化、Webhook secret 加密存储 |
---
## 后续可执行动作
1. **文档修正**:先修订 `BugCollect-API-v1.md`,把当前未实现项标注为「未实现/TODO」,把字段 mismatch 统一成实现侧也认可的版本。
2. **服务端补齐**:按修订后的文档修复 `LogController`、`LogService`、`WebhookService`、`SourcemapService`、Flyway 迁移。
3. **SDK 补齐**Android 保持现状,RN SDK 补齐 fatal/breadcrumbs/device;iOS/HarmonyOS/Flutter/Web 按排期推进。
4. **租户平台同步**:更新 `tenant-platform/src/api/bugcollect.ts` 类型和接口封装。
5. **OpenAPI 化**:在 `docs/` 下新增 `BugCollect-API-v1.yaml`,后续作为唯一事实来源。