7.2 KiB
7.2 KiB
重构总结:复用现有 Umami 工具
Date: 2025-12-05
Issue: 发现重复代码 - 新实现的 UmamiAdapter 与现有 umami-analytics.ts 功能重叠
问题发现
在实现声明式埋点系统时,发现以下重复:
原有实现
- 位置:
packages/drawnix/src/utils/umami-analytics.ts - 内容: 完整的 UmamiAnalytics 工具类
window.umami接口定义- 基础
track()方法 - AI 生成事件专用方法
- 用户交互、特性使用追踪
- 统一的日志输出
[Analytics]
新实现(重复部分)
- 位置:
packages/drawnix/src/services/tracking/umami-adapter.ts - 重复内容:
- ❌ 重复定义
window.umami接口 - ❌ 重复实现
track()基础功能 - ❌ 重复 SDK 可用性检查逻辑
- ❌ 重复定义
重构方案
修改前的架构
声明式埋点
↓
UmamiAdapter (直接调用 window.umami)
↓
window.umami.track()
↓
Umami 服务器
修改后的架构(复用现有工具)
声明式埋点
↓
UmamiAdapter (适配器层)
↓
analytics.track() (复用现有 UmamiAnalytics)
↓
window.umami.track()
↓
Umami 服务器
代码变更
1. UmamiAdapter 重构
文件: packages/drawnix/src/services/tracking/umami-adapter.ts
Before:
// 直接使用 window.umami
export class UmamiTrackingAdapter {
isAvailable(): boolean {
return typeof window.umami !== 'undefined';
}
async track(event: TrackEvent): Promise<void> {
await window.umami!.track(event.eventName, enrichedData);
}
}
After:
// 复用现有 analytics 单例
import { analytics } from '../../utils/umami-analytics';
export class UmamiTrackingAdapter {
isAvailable(): boolean {
return analytics.isAnalyticsEnabled(); // ✅ 复用现有方法
}
async track(event: TrackEvent): Promise<void> {
analytics.track(event.eventName, enrichedData); // ✅ 复用现有方法
}
}
变更说明:
- ✅ 删除了
window.umami接口定义(使用 umami-analytics.ts 中的定义) - ✅ 调用
analytics.track()而不是window.umami.track() - ✅ 使用
analytics.isAnalyticsEnabled()检查 SDK 可用性 - ✅ 保留了元数据注入逻辑(声明式埋点特有)
2. 测试文件重构
文件: packages/drawnix/src/services/tracking/__tests__/umami-adapter.test.ts
Before:
// Mock window.umami
const mockUmami = {
track: jest.fn().mockResolvedValue(undefined),
};
(global as any).window = {
umami: mockUmami,
};
After:
// Mock analytics utility
jest.mock('../../../utils/umami-analytics', () => ({
analytics: {
track: jest.fn(),
isAnalyticsEnabled: jest.fn(),
},
}));
// 使用 analytics mock 进行测试
expect(analytics.track).toHaveBeenCalledWith('test_event', {...});
变更说明:
- ✅ Mock
analytics而不是window.umami - ✅ 验证
analytics.track()被正确调用 - ✅ 验证
analytics.isAnalyticsEnabled()被正确使用 - ✅ 所有测试用例更新完毕
3. 文档更新
文件: specs/005-declarative-tracking/contracts/umami-api.md
- ✅ 更新集成策略说明
- ✅ 更新示例代码
- ✅ 添加集成优势说明
文件: specs/005-declarative-tracking/INTEGRATION.md (新增)
- ✅ 详细说明两种埋点方式的差异
- ✅ 使用场景建议
- ✅ 数据流对比
- ✅ 元数据对比
- ✅ 混合使用示例
重构收益
代码质量
| 指标 | 重构前 | 重构后 | 改进 |
|---|---|---|---|
| 代码重复 | 是(window.umami 接口定义重复) | 否 | ✅ 消除重复 |
| 依赖管理 | 直接依赖 window.umami | 依赖 analytics 单例 | ✅ 统一依赖 |
| 日志格式 | 不一致([Tracking] vs [Analytics]) | 统一通过 analytics | ✅ 日志统一 |
| 错误处理 | 独立实现 | 复用 analytics | ✅ 逻辑统一 |
维护性
- ✅ 单一数据源: 只有
umami-analytics.ts定义 Umami 接口 - ✅ 自动受益: analytics 的任何改进会自动惠及声明式埋点
- ✅ 降低维护成本: 减少需要维护的代码量
- ✅ 提高可测试性: Mock analytics 比 Mock window.umami 更简单
一致性
- ✅ 日志格式统一: 都通过 analytics 输出,格式一致
- ✅ 错误处理统一: 复用 analytics 的 try-catch 逻辑
- ✅ SDK 检查统一: 都使用
analytics.isAnalyticsEnabled()
未来优化方向
虽然已经消除了重复代码,但仍有改进空间:
1. 统一元数据注入
当前:
- 手动埋点:
timestamp(由 analytics 注入) - 声明式埋点:
version,url,sessionId,viewport,eventType(由 adapter 注入)
优化建议:
考虑在 UmamiAnalytics 中统一注入所有元数据,让两种埋点方式都受益
// 在 UmamiAnalytics.track() 中统一注入
track(eventName: string, eventData?: Record<string, any>): void {
const enrichedData = {
...eventData,
timestamp: Date.now(),
version: this.getVersion(), // 新增
url: window.location.href, // 新增
sessionId: this.getSessionId(), // 新增
};
window.umami.track(eventName, enrichedData);
}
2. 统一批量上报
当前: 只有声明式埋点支持批量上报
优化建议: 考虑为 UmamiAnalytics 也添加批量上报能力
class UmamiAnalytics {
private batchService: BatchService;
track(eventName: string, eventData?: Record<string, any>): void {
if (this.config.enableBatch) {
this.batchService.enqueue({eventName, eventData});
} else {
window.umami.track(eventName, eventData);
}
}
}
3. 统一离线缓存
当前: 只有声明式埋点支持离线缓存
优化建议: 考虑为所有埋点提供统一的离线缓存支持
4. 统一配置管理
当前:
- UmamiAnalytics: 无配置
- TrackingService: TrackConfig (日志级别、重试策略、批量配置等)
优化建议:
考虑创建统一的 AnalyticsConfig 供两种方式共享
验证清单
功能验证
- UmamiAdapter 正确调用
analytics.track() - UmamiAdapter 正确使用
analytics.isAnalyticsEnabled() - 所有单元测试通过(使用 analytics mock)
- 元数据正确注入(version, url, sessionId, viewport, eventType)
- 批量上报功能正常
- 离线缓存功能正常
- 手动埋点未受影响
代码质量验证
- 无
window.umami接口重复定义 - 无基础
track()方法重复实现 - 日志输出使用
[Tracking]前缀区分 - 所有文件符合 <500 行限制
- TypeScript 类型检查通过
文档验证
- umami-api.md 更新集成策略说明
- INTEGRATION.md 详细说明两种埋点方式
- quickstart.md 示例正确
总结
通过这次重构:
- 消除了代码重复 - UmamiAdapter 现在是现有 analytics 工具的适配器
- 保持了功能完整 - 声明式埋点的所有特性(批量、缓存、防抖、元数据)都保留
- 提高了一致性 - 两种埋点方式共享底层实现
- 降低了维护成本 - 减少了需要维护的代码量
- 提高了可测试性 - 测试更简单、更可靠
这是一次成功的重构,既解决了代码重复问题,又为未来的统一埋点系统铺平了道路。