Initial TrueGrowth source import
This commit is contained in:
272
specs/005-declarative-tracking/REFACTORING.md
Normal file
272
specs/005-declarative-tracking/REFACTORING.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 重构总结:复用现有 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**:
|
||||
```typescript
|
||||
// 直接使用 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**:
|
||||
```typescript
|
||||
// 复用现有 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**:
|
||||
```typescript
|
||||
// Mock window.umami
|
||||
const mockUmami = {
|
||||
track: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
(global as any).window = {
|
||||
umami: mockUmami,
|
||||
};
|
||||
```
|
||||
|
||||
**After**:
|
||||
```typescript
|
||||
// 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` 中统一注入所有元数据,让两种埋点方式都受益
|
||||
|
||||
```typescript
|
||||
// 在 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 也添加批量上报能力
|
||||
|
||||
```typescript
|
||||
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` 供两种方式共享
|
||||
|
||||
## 验证清单
|
||||
|
||||
### 功能验证
|
||||
|
||||
- [X] UmamiAdapter 正确调用 `analytics.track()`
|
||||
- [X] UmamiAdapter 正确使用 `analytics.isAnalyticsEnabled()`
|
||||
- [X] 所有单元测试通过(使用 analytics mock)
|
||||
- [X] 元数据正确注入(version, url, sessionId, viewport, eventType)
|
||||
- [X] 批量上报功能正常
|
||||
- [X] 离线缓存功能正常
|
||||
- [X] 手动埋点未受影响
|
||||
|
||||
### 代码质量验证
|
||||
|
||||
- [X] 无 `window.umami` 接口重复定义
|
||||
- [X] 无基础 `track()` 方法重复实现
|
||||
- [X] 日志输出使用 `[Tracking]` 前缀区分
|
||||
- [X] 所有文件符合 <500 行限制
|
||||
- [X] TypeScript 类型检查通过
|
||||
|
||||
### 文档验证
|
||||
|
||||
- [X] umami-api.md 更新集成策略说明
|
||||
- [X] INTEGRATION.md 详细说明两种埋点方式
|
||||
- [X] quickstart.md 示例正确
|
||||
|
||||
## 总结
|
||||
|
||||
通过这次重构:
|
||||
|
||||
1. **消除了代码重复** - UmamiAdapter 现在是现有 analytics 工具的适配器
|
||||
2. **保持了功能完整** - 声明式埋点的所有特性(批量、缓存、防抖、元数据)都保留
|
||||
3. **提高了一致性** - 两种埋点方式共享底层实现
|
||||
4. **降低了维护成本** - 减少了需要维护的代码量
|
||||
5. **提高了可测试性** - 测试更简单、更可靠
|
||||
|
||||
这是一次成功的重构,既解决了代码重复问题,又为未来的统一埋点系统铺平了道路。
|
||||
Reference in New Issue
Block a user