Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

View 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. **提高了可测试性** - 测试更简单、更可靠
这是一次成功的重构,既解决了代码重复问题,又为未来的统一埋点系统铺平了道路。