5.7 KiB
5.7 KiB
任务中心领取 Bug 修复计划
问题描述
小程序(bindbox-mini)任务中心,活动前端领取不了但是可以看到。
根因分析
核心问题:前后端进度数据源不一致
后端 ClaimTier(service.go:738)使用 TierProgressMap(基于窗口化、受任务时间约束的进度)来校验是否达标:
if tp, ok := progress.TierProgressMap[tierID]; ok {
currentOrderCount = tp.OrderCount // 窗口化进度,受任务 StartTime 约束
}
前端 isTierClaimable(index.vue:387-437)使用的是 subProgress(活动级别汇总)或全局进度(orderCount / orderAmount),完全没有使用 tier_progress_map。
触发条件
最近提交 e0db875 修改了 computeTimeWindow:
- 修改前:
lifetime/since_registration/ 空窗口 →return nil, nil(不限时间) - 修改后:
lifetime/ 默认窗口 →return taskStart, taskEnd(受任务时间约束)
这导致 TierProgressMap 中的进度值被任务时间限制,但 API 返回的 order_count / sub_progress 全局进度仍然不受时间限制(service.go:618-622 用 nil, nil 查询)。
不一致的结果
| 数据源 | 时间约束 | 进度值 | 使用方 |
|---|---|---|---|
TierProgressMap |
受任务 StartTime/EndTime 约束 | 较小 | 后端 ClaimTier |
SubProgress / 全局进度 |
无时间约束 | 较大 | 前端 isTierClaimable |
API 返回的 order_count |
无时间约束(或活动级别) | 较大 | 前端 isTierClaimable |
场景举例:
- 用户在任务创建前有 5 笔历史订单,任务创建后有 2 笔新订单
- 任务档位要求
order_count >= 3 - 前端看到全局
orderCount = 7(不限时间) → 显示"领取"按钮 - 后端
TierProgressMap.OrderCount = 2(只统计任务开始后) → 返回"任务条件未达成"
或者反过来:
- 前端也使用
subProgress做判断,但subProgress的统计可能不包含某些场景的数据 - 导致前端
isTierClaimable返回false,按钮不出现 - 用户看到任务但无法领取
任务类型
- 后端 (→ 后端逻辑修复)
- 前端 (→ 前端判断修复)
技术方案
方案 A(推荐):后端 API 返回 tier_progress_map,前端使用
让前后端使用同一份进度数据源(TierProgressMap),确保判断一致。
实施步骤
Step 1:后端 - API Response 增加 tier_progress_map 字段
文件: internal/api/task_center/tasks_app.go
- 在
taskProgressResponse结构体中添加TierProgressMap字段:
type tierProgressItem struct {
TierID int64 `json:"tier_id"`
OrderCount int64 `json:"order_count"`
OrderAmount int64 `json:"order_amount"`
InviteCount int64 `json:"invite_count"`
FirstOrder bool `json:"first_order"`
}
type taskProgressResponse struct {
// ... existing fields ...
TierProgress []tierProgressItem `json:"tier_progress"` // 新增
}
- 在
GetTaskProgressForApphandler 中填充该字段。
Step 2:前端 - isTierClaimable 优先使用 tier_progress
文件: bindbox-mini/pages-user/tasks/index.vue
- 在
fetchData中解析并存储tier_progress到taskProgress[taskId] - 修改
isTierClaimable函数,优先从tierProgress中查找对应 tier 的进度 - 修改
getTierProgressText和getTierProgressPercent,同步使用新数据源
function isTierClaimable(task, tier) {
const progress = taskProgress[task.id] || {}
// 优先使用 tier 级别窗口化进度(与后端 ClaimTier 保持一致)
if (progress.tierProgress) {
const tp = progress.tierProgress.find(t => t.tier_id === tier.id)
if (tp) {
const metric = tier.metric || ''
const threshold = tier.threshold || 0
const operator = tier.operator || '>='
let current = 0
if (metric === 'first_order') return tp.first_order || false
else if (metric === 'order_count') current = tp.order_count || 0
else if (metric === 'order_amount') current = tp.order_amount || 0
else if (metric === 'invite_count') current = tp.invite_count || 0
if (operator === '>=') return current >= threshold
if (operator === '==') return current === threshold
if (operator === '>') return current > threshold
return current >= threshold
}
}
// fallback: 原有逻辑
// ...
}
Step 3:同步修改进度显示
修改 getTierProgressText 和 getTierProgressPercent 也优先使用 tierProgress 数据,确保用户看到的进度和可领取状态一致。
关键文件
| 文件 | 操作 | 说明 |
|---|---|---|
internal/api/task_center/tasks_app.go:106-170 |
修改 | 添加 tier_progress 到响应体 |
bindbox-mini/pages-user/tasks/index.vue:387-437 |
修改 | isTierClaimable 使用 tier_progress |
bindbox-mini/pages-user/tasks/index.vue:440-478 |
修改 | getTierProgressText 使用 tier_progress |
bindbox-mini/pages-user/tasks/index.vue:590-630 |
修改 | getTierProgressPercent 使用 tier_progress |
bindbox-mini/pages-user/tasks/index.vue:551-576 |
修改 | fetchData 解析 tier_progress |
风险与缓解
| 风险 | 缓解措施 |
|---|---|
前端旧版本未使用 tier_progress 字段 |
保持原有 order_count、sub_progress 字段不变,tier_progress 为新增字段,向后兼容 |
tier_progress_map 为空(数据库无 tiers 配置) |
前端 fallback 到原有 subProgress / 全局进度逻辑 |
| 已部署但未刷新前端的用户 | tier_progress 是附加字段,不影响旧逻辑 |
SESSION_ID(供 /ccg:execute 使用)
- CODEX_SESSION: N/A(未使用外部模型)
- GEMINI_SESSION: N/A(未使用外部模型)