- 将 Android SDK 的 mapping 上传端点从 /log/v1/sourcemaps/upload 更改为 /bugcollect/v1/sourcemaps/upload - 将 RN SDK 的 API 端点从 /log/v1/ 统一更改为 /bugcollect/v1/ - 在 LogQueue.ts 的请求体中添加 sentAt 时间戳和 SDK 信息 - 重构 BugCollect.ts 中的事件结构,将 appVersion 重命名为 release,添加 environment 和 sdk 字段 - 将 JS 错误上报的类型从 js_error 改为 issue,并调整错误级别分类 - 为 warn 和 info 方法添加完整的 issue 事件结构 - 在 types.ts 中添加新的数据类型定义,包括 Level、Platform、SdkInfo、ExceptionInfo 等 - 为 IssueEvent 添加详细的异常信息结构,包括类型、值和堆栈跟踪 - 添加完整的错误收集 API v1 规范审阅报告文档 - 在数据库迁移脚本中为日志表添加新字段,包括级别、环境、设备信息、SDK 信息等
14 KiB
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需要保留,也写进文档。
- 统一 Webhook 模型:按文档实现
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 仍返回已删除字段
- 文档
13Breaking 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:
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 当前缺失:
addBreadcrumbAPI(文档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 加密存储 |
后续可执行动作
- 文档修正:先修订
BugCollect-API-v1.md,把当前未实现项标注为「未实现/TODO」,把字段 mismatch 统一成实现侧也认可的版本。 - 服务端补齐:按修订后的文档修复
LogController、LogService、WebhookService、SourcemapService、Flyway 迁移。 - SDK 补齐:Android 保持现状,RN SDK 补齐 fatal/breadcrumbs/device;iOS/HarmonyOS/Flutter/Web 按排期推进。
- 租户平台同步:更新
tenant-platform/src/api/bugcollect.ts类型和接口封装。 - OpenAPI 化:在
docs/下新增BugCollect-API-v1.yaml,后续作为唯一事实来源。