# Widget 埋点测试方法总结

**日期**: 2026-03-20
**基于案例**: 活动6392（欧洲-樱之神国）Widget埋点验证

---

## 一、Widget 埋点的代码位置规律

### 1.1 通用 Widget 埋点 ⭐⭐⭐

**位置**: `wespy-http-go/app/activity/common/acttrack/`

这些是**跨活动复用**的Widget埋点，最常见，优先在此目录查找。

| Widget类型 | 文件名 | 核心函数 | activity_type |
|-----------|--------|---------|---------------|
| 任务系统 | `task.go` | TrackTaskCompleted, TrackTaskRecvReward | task |
| 抽奖系统 | `lottery.go` | TrackDoLottery | lottery |
| 碎片收集 | `collect_chip.go` | TrackCollectChipAddChip | chip |
| 碎片兑换 | `exchange_store.go` | TrackExchangeGood | chip |
| 礼盒奖励 | `gift_box.go` | TrackGiftBoxGetReward | gift_box |
| 盲盒奖励 | `blind_box.go` | TrackBlindBoxGetReward | blind_box |
| 送礼触发 | `send_gift.go` | TrackSendGift ⭐ | gift |
| 充值优惠 | `charge_coupon.go` | TrackChargeCouponIncrCoupon | charge_coupon |

**查找命令**:
```bash
cd wespy-http-go/app/activity/common/acttrack/
ls -la | grep -E "task|lottery|chip|gift|coupon"
```

---

### 1.2 特定 Widget 埋点 ⭐⭐

**位置**: `wespy-http-go/app/activity/widget/{widget_name}/acttrack/`

某些Widget有自己的专属埋点实现，通常在Widget目录下有独立的 `acttrack/track.go`。

| Widget类型 | 路径 | 核心函数 |
|-----------|------|---------|
| 红包雨 | `widget/redpacket/acttrack/track.go` | TrackRedPacketTrigger |
| 充值优惠 | `widget/charge_coupon/event/event.go` | 事件处理，调用通用埋点 |

**查找命令**:
```bash
cd wespy-http-go/app/activity/widget/
find . -name "acttrack" -type d
find . -path "*/acttrack/track.go"
```

---

### 1.3 活动专属埋点

**位置**: `wespy-http-go/app/activity/{year}/{region}/{activity_name}/internal/track/`

活动特有的埋点，不是Widget。

**示例**: `2026/europe/par_sakura_kingdom/internal/track/track.go`
- TrackChooseCup
- TrackNextChoose
- TrackReviveSuccess

---

## 二、Widget 埋点测试流程 ✅

### 步骤1: 确认Widget类型

从测试用例的 `activity_type` 或 `action` 判断Widget类型：

| activity_type | 对应Widget | 搜索关键词 |
|--------------|-----------|----------|
| task | 任务系统 | task.go |
| lottery | 抽奖系统 | lottery.go |
| chip | 碎片系统 | collect_chip.go, exchange_store.go |
| gift | 礼物系统 | send_gift.go |
| charge_coupon | 充值优惠 | charge_coupon.go |
| blind_box | 盲盒 | blind_box.go |
| gift_box | 礼盒 | gift_box.go |

---

### 步骤2: 快速定位代码

**方法1: 直接查看通用埋点** (90%的情况)
```bash
cd wespy-http-go/app/activity/common/acttrack/
cat {widget_type}.go
```

**方法2: 全局搜索action名称**
```bash
cd wespy-http-go/app/activity
grep -rn "action.*\"complete_task\"" common/acttrack/
grep -rn "action.*\"do_wheel_lottery\"" common/acttrack/
```

**方法3: 搜索Widget目录**
```bash
cd wespy-http-go/app/activity/widget/
grep -rn "TrackRedPacket" .
```

---

### 步骤3: 验证配置存在

使用 `get_key_api.py` 查询配置中心：

```python
python3 scripts/get_key_api.py
# 输入 key: {activity_id}
# 输入 region: R/C/A/E/J/K
```

检查配置中是否存在对应的Widget配置：
- `charge_coupon` - 充值优惠
- `lottery` - 抽奖池
- `tasks` - 任务列表
- `collect_chip` - 碎片配置
- `exchange_store` - 兑换商城
- `red_packet` - 红包雨
- `send_gift_hooker_v2` - 送礼触发器

---

### 步骤4: 查看代码实现

**必查内容**:
1. ✅ 埋点函数名称（如 `TrackDoLottery`）
2. ✅ 参数结构体定义（如 `LotteryReward`）
3. ✅ 上报的字段列表（map[string]interface{}）
4. ✅ action 固定值或动态逻辑
5. ✅ activity_type 固定值

**代码模板**:
```go
func TrackXxx(actId, uid int, data XxxData) {
    SensorTrack(int64(uid), "ActivityTotal", commonacttrack.MergeActivityTrack(map[string]interface{}{
        "activity_type": "xxx",      // ✅ 验证activity_type
        "action":        "xxx",       // ✅ 验证action
        "act_id":        actId,       // ✅ 验证act_id
        "field1":        data.Field1, // ✅ 逐一验证字段
        // ...
    }), user.GetUserSaProperties(int32(uid)))
}
```

---

### 步骤5: 查找调用位置

**方法1: 搜索函数调用**
```bash
cd wespy-http-go/app/activity
grep -rn "TrackDoLottery" .
grep -rn "acttrack.Track" .
```

**方法2: 查看Widget事件处理**
```bash
cat widget/{widget_name}/event/event.go
cat widget/{widget_name}/service/service.go
```

**方法3: 查看活动handler**
```bash
cat 2026/europe/par_sakura_kingdom/internal/handler/handler.go
```

---

## 三、常见Widget埋点模式 🎯

### 模式1: 标准模式 (最常见)

**特点**: 一个函数对应一个埋点，action固定

**示例**: 任务埋点
```go
// task.go
func TrackTaskCompleted(actId, uid int, data TaskCompletedData) {
    SensorTrack(..., map[string]interface{}{
        "action": "complete_task",  // 固定值
        "activity_type": "task",
        // ...
    })
}
```

**测试要点**:
- ✅ 直接读取函数，验证所有字段
- ✅ 确认action和activity_type固定值
- ✅ 验证数据结构体字段映射

---

### 模式2: 智能切换模式 ⭐⭐⭐

**特点**: **一个函数根据参数动态决定action值**

**示例**: 送礼埋点（活动6392验证）
```go
// send_gift.go:41-66
func TrackSendGift(actId int, uid int, param SendGiftData) {
    action := "send_gift"           // 默认
    if param.IsGuarantee {
        action = "guarantee"        // 保底
    } else if param.IsRandom {
        action = "random"           // 随机
    }
    SensorTrack(..., map[string]interface{}{
        "action": action,           // 动态！
        // ...
    })
}
```

**测试要点**:
- ✅ 识别动态切换逻辑
- ⚠️ 不要误认为是3个独立函数
- ✅ 验证切换条件（IsGuarantee/IsRandom）
- ✅ 测试每个分支的action值

**识别方法**:
```bash
# 搜索时发现多个action但只有一个函数
grep -n "action.*\"random\|guarantee\|send_gift\"" send_gift.go
```

---

### 模式3: 自动计算模式

**特点**: 某些字段由埋点函数自动计算，不需要传参

**示例1**: 抽奖埋点 - 自动计算 `use_lottery_coins_num`
```go
// lottery.go
var totalPrice int
for k, v := range data.ChipCostMap {
    price, _ := actreward.GetRewardPrice(...)
    totalPrice += int(price)
}
trackData["use_lottery_coins_num"] = totalPrice  // ✅ 自动计算
```

**示例2**: 碎片兑换 - 自动获取 `chip_name`
```go
// exchange_store.go
chipName := actreward.GetChipName(actId, data.ChipID)  // ✅ 自动获取
trackData["chip_name"] = chipName
```

**示例3**: 任务埋点 - 自动获取团队信息
```go
// collect_chip.go
teamID := activity.GetUserTeamID(int32(actId), int32(uid))
// ... 自动填充 team_key, team_num, uids
```

**测试要点**:
- ✅ 识别哪些字段是自动计算的
- ✅ 验证计算逻辑是否正确
- ⚠️ 不要期望在参数结构体中找到这些字段

---

### 模式4: 事件触发模式

**特点**: Widget通过事件系统自动触发，不在活动代码中显式调用

**示例**: 充值优惠埋点
```go
// widget/charge_coupon/event/event.go:78
func tryTrackIncrCoupon(...) {
    // 判断条件满足
    acttrack.TrackChargeCouponIncrCoupon(actId, uid, times)
}

// 事件订阅
func HandleChargeCoupon(eventData string) {
    // 解析事件
    tryTrackIncrCoupon(...)
}
```

**调用链路**:
```
用户充值/消耗金币
  → 发布事件 actevent.CouponEvent
  → HandleChargeCoupon()
  → tryTrackIncrCoupon()
  → TrackChargeCouponIncrCoupon()
```

**测试要点**:
- ✅ 在 `widget/{name}/event/` 查找事件处理
- ✅ 验证事件订阅和触发条件
- ⚠️ 不要只在活动代码中搜索函数名

---

## 四、快速测试检查表 📋

### 基础检查（必做）

- [ ] 1. 确认配置中心有对应Widget配置
- [ ] 2. 找到埋点函数位置（通用/Widget/活动）
- [ ] 3. 验证 `activity_type` 固定值
- [ ] 4. 验证 `action` 值（固定/动态）
- [ ] 5. 逐一验证所有字段映射
- [ ] 6. 确认字段类型（string/int/bool）

### 高级检查（推荐）

- [ ] 7. 查找函数调用位置
- [ ] 8. 验证事件触发机制（如有）
- [ ] 9. 检查自动计算字段的逻辑
- [ ] 10. 验证动态切换逻辑（如有）
- [ ] 11. 确认团队信息自动获取（如有）
- [ ] 12. 检查异步上报（safego.SafeGo）

### 测试用例生成

- [ ] 13. 基础字段完整性（每个字段一个用例）
- [ ] 14. 枚举值验证（如 period_type=0/1）
- [ ] 15. 动态action验证（如 random/guarantee/send_gift）
- [ ] 16. 触发时机验证
- [ ] 17. 上报次数验证
- [ ] 18. 边界场景（is_free=0/1, time_limit=0/>0）

---

## 五、常见问题解决 🔧

### 问题1: 找不到埋点代码 ❓

**解决步骤**:
1. ✅ 先查 `common/acttrack/` （90%在这里）
2. ✅ 再查 `widget/{name}/acttrack/`
3. ✅ 搜索 action 名称: `grep -rn "action.*\"xxx\"" .`
4. ✅ 搜索函数名: `grep -rn "Track.*Xxx" .`
5. ⚠️ 如果还找不到，可能不是Widget埋点，或者该功能未实现

---

### 问题2: 配置存在但代码未找到 ❓

**可能原因**:
1. **事件触发**: 在 `widget/{name}/event/` 查看事件处理
2. **全局钩子**: 由送礼、支付等全局系统自动触发
3. **未实现**: 配置了但代码还没写（需开发确认）

**验证方法**:
```bash
# 查找事件处理
find widget/ -name "event.go"
grep -rn "Handle.*Event" widget/

# 查找全局钩子
grep -rn "send_gift_hooker" .
grep -rn "red_packet" widget/
```

---

### 问题3: 一个埋点对应多个action ❓

**识别智能切换模式**:
```bash
cd common/acttrack/
grep -A20 "func Track.*Gift" send_gift.go
# 看到 if...else 切换 action 变量
```

**验证要点**:
- ✅ 确认是同一个函数
- ✅ 找到切换条件（IsGuarantee/IsRandom等）
- ✅ 验证优先级（if-else顺序）
- ✅ 测试每个分支

---

### 问题4: 字段在参数中找不到 ❓

**可能是自动计算字段**:

**常见自动字段**:
- `use_lottery_coins_num` - 由 `ChipCostMap` 计算金币价值
- `chip_name` - 从配置中心自动获取
- `team_key`, `team_num`, `uids` - 自动查询团队信息
- `activity`, `activity_name` - 由 `MergeActivityTrack` 自动添加

**验证方法**:
1. 在埋点函数内搜索字段名
2. 找到计算/查询逻辑
3. 验证数据来源（配置/数据库/计算）

---

## 六、测试报告模板 📄

### Widget埋点测试结构

```markdown
### 📋 #{编号}: {埋点名称}

**代码位置**: [文件名:行号](路径)

| 用例ID | 测试场景 | 预期结果 | 预测结果（代码审查） | 状态 | 问题 |
|--------|---------|---------|---------------------|------|------|
| TC{N}-01 | action固定值 | action="xxx" | "xxx"✅ (文件:行号) | ✅ | - |
| TC{N}-02 | activity_type固定值 | activity_type="xxx" | "xxx"✅ | ✅ | - |
| TC{N}-03 | 包含字段1 | field1=值 | data.Field1✅ | ✅ | - |
| ... | ... | ... | ... | ... | ... |

**小计**: X个用例，✅ **X个通过**

**代码验证**:
```go
// 文件路径:行号
func TrackXxx(...) {
    // 关键代码
}
```

**调用位置**:
- 位置1: 说明
- 位置2: 说明

#### 📊 测试数据示例

**操作步骤**:
```
1. ...
2. ...
```

**预期埋点数据（JSON格式）**:
```json
{
  "event_name": "ActivityTotal",
  "distinct_id": "...",
  "properties": {
    ...
  }
}
```

**关键验证点**:
- ✅ 验证点1
- ✅ 验证点2
```

---

## 七、实战案例：活动6392 Widget埋点验证 🎓

### 案例1: 任务埋点 (标准模式)

**耗时**: 5分钟
**步骤**:
1. 看到 `activity_type="task"` → 查 `common/acttrack/task.go`
2. 找到 `TrackTaskCompleted` 和 `TrackTaskRecvReward`
3. 验证所有字段映射 → ✅ 通过

---

### 案例2: 送礼埋点 (智能切换模式)

**耗时**: 15分钟
**步骤**:
1. 看到3个埋点: random/guarantee/send_gift
2. 搜索发现都在 `send_gift.go` 的同一个函数
3. 发现 `if-else` 切换逻辑 → **关键发现！**
4. 验证3种场景 → ✅ 全部通过

**教训**: 不要假设3个action = 3个函数

---

### 案例3: 红包雨埋点 (Widget目录)

**耗时**: 10分钟
**步骤**:
1. 在 `common/acttrack/` 未找到
2. 搜索 `widget/redpacket/` → 找到 `acttrack/track.go`
3. 验证字段 → ✅ 通过

**教训**: 某些Widget有独立目录

---

### 案例4: 充值优惠埋点 (事件触发)

**耗时**: 20分钟
**步骤**:
1. 找到 `common/acttrack/charge_coupon.go` → TrackChargeCouponIncrCoupon
2. 搜索调用位置 → 找到 `widget/charge_coupon/event/event.go:78`
3. 验证事件处理逻辑 → ✅ 通过
4. use_coupon在活动代码中未找到 → ⚠️ 待确认

**教训**: 调用链路可能在事件处理中

---

## 八、工具脚本 🛠️

### 快速搜索Widget埋点

```bash
#!/bin/bash
# 文件名: search_widget_tracking.sh

ACTIVITY_TYPE=$1

if [ -z "$ACTIVITY_TYPE" ]; then
    echo "用法: ./search_widget_tracking.sh <activity_type>"
    echo "示例: ./search_widget_tracking.sh task"
    exit 1
fi

echo "========================================="
echo "搜索 activity_type=\"$ACTIVITY_TYPE\" 的Widget埋点"
echo "========================================="

cd wespy-http-go/app/activity

echo ""
echo "1. 通用埋点目录:"
grep -rn "activity_type.*\"$ACTIVITY_TYPE\"" common/acttrack/ | head -20

echo ""
echo "2. Widget目录:"
grep -rn "activity_type.*\"$ACTIVITY_TYPE\"" widget/ | head -20

echo ""
echo "3. 函数定义:"
grep -rn "func Track.*(" common/acttrack/ widget/ | grep -i "$ACTIVITY_TYPE" | head -10
```

---

### 验证配置和代码一致性

```bash
#!/bin/bash
# 文件名: verify_widget_config.sh

ACTIVITY_ID=$1
REGION=$2

echo "查询活动 $ACTIVITY_ID (区服: $REGION)"
python3 scripts/get_key_api.py <<EOF
$ACTIVITY_ID
$REGION
EOF

# 检查常见Widget配置
echo ""
echo "检查Widget配置:"
echo "- charge_coupon: 充值优惠"
echo "- lottery: 抽奖"
echo "- tasks: 任务"
echo "- collect_chip: 碎片"
echo "- exchange_store: 兑换"
echo "- red_packet: 红包雨"
```

---

## 九、总结与建议 ✨

### 核心经验

1. **90%的Widget埋点在 `common/acttrack/`** - 优先查这里
2. **智能切换模式需特别注意** - 一个函数多个action
3. **配置中心必查** - 确认Widget功能已开启
4. **事件触发要追溯** - 调用链可能在event handler
5. **自动字段要识别** - 不是所有字段都在参数中

### 效率提升

- ✅ 使用搜索脚本批量查找
- ✅ 建立Widget类型到文件的映射表
- ✅ 记录常见模式和特例
- ✅ 复用成功案例的测试结构

### 质量保证

- ✅ Widget埋点和普通埋点一样要详细测试
- ✅ 不能因为是Widget就假设正确
- ✅ 必须查看实际代码实现
- ✅ 验证完整的调用链路

---

**最后更新**: 2026-03-20
**维护者**: Claude Code
**参考活动**: 6392 (欧洲-樱之神国) - 89% Widget埋点验证通过率
