# 04 助力系统（for AI Agents）

> 关联代码：internal/service/assist/assist.go、internal/handler/mp/assist.go、internal/response/lottery_codes.go（2180 段）
> 管理端页面：web/src/views/config/assist-rules/；小程序：pages/lottery/detail/assist-panel/（组件，非独立页面）
> 2026-09 修复（已合入 master，commit 727f040）：助力开关后端强制生效、任务链路/倒计时/文案合规、创建端开关语义改为「禁止助力」默认开启。

====================================================================
1. 功能概述
====================================================================

助力是普通抽奖的参与后玩法：用户参与抽奖后，通过完成任务（浏览页面、看激励视频、心愿兑换）累积助力值，进入该抽奖的助力榜排名。合规红线：助力值只影响排行榜名次与倍数展示，不参与开奖权重，任何文案不得承诺「提升中奖率」。

开关：Lottery.Config JSON 的 enable_assist。创建端（后台表单与小程序创建页）的开关语义是「禁止助力」（默认关闭=助力开启）。

====================================================================
2. 底层逻辑
====================================================================

2.1 任务定义（internal/service/assist/assist.go 硬编码 DefaultTasks）
- daily_browse：每日任务，浏览指定页面满 15 秒，+1 助力值
- watch_video：一次性任务，完整观看激励视频，+3 助力值
- 心愿兑换（wish exchange）：可重复（cumulative），消耗 1 心愿值（积分）得 5 助力值；在 DoAssist 事务内扣积分，余额不足整单回滚（错误码 2181）
- 已移除 redpacket_flip 任务（小程序端无「翻红包」玩法，任务永远不可完成）

2.2 记录与幂等
- AssistRecord 唯一键 uk_assist_record(lottery_id, mp_user_id, task_code, period)；period 取值：daily=自然日期、once=固定串、cumulative=seq-N 槽位（第 N 次兑换占一个槽）。
- 重复完成同任务被唯一键拒绝（幂等）；每日任务次日换 period 可再做。
- IsDoubled 标记 + 限时翻倍：参与后存在一个限时翻倍窗口（assist_status 返回 assist_deadline 与 assist_deadline_enabled），窗口内完成任务助力值翻倍；翻倍时长当前无后台配置 UI（代码内固定）。

2.3 计数与榜单
- 每次有效助力累加 Participant.assist_count。
- 助力榜 = GET /mp/lottery/:id/participants/leaderboard，按 assist_count 排序；倍数（multiplier）仅展示用。

2.4 门槛与错误码
- 未参与该抽奖不可助力；活动未开助力（enable_assist=false）时后端 doAssist 直接拒绝：2180「该活动未开启助力」（小程序参与区/面板不出现，右滑手势也不触发）。
- 心愿值不足：2181。
- 助力接口非参与入口，错误码不进 join 码表，由 handler/mp/assist.go 直接映射。

2.5 事件
- 助力完成会发 assist.done 事件，当前无消费者（预留扩展）。

====================================================================
3. 后台操作
====================================================================

配置管理 → 助力规则（/config/assist-rules，views/config/assist-rules/）
- 规则存 KV 表，键 rule_1 ~ rule_6；每条含 key / 标题(≤30 字) / 正文(≤500 字)。
- 「新增规则」；表（key/标题/正文摘要/更新时间/操作）；行操作：编辑/删除。小程序助力面板按序展示这些规则文案。
- 注意：首次部署时种子写入默认 6 条规则；seed 只在全新库执行，存量库不会自动补齐/更新规则内容，需人工在此页维护。

后台抽奖表单（/lotteries/create）中的「禁止助力」开关即本系统入口：默认不勾选=助力开启；勾选后该活动无助力面板。

3.1 后台操作逻辑备注（隐藏约束，UI 不直接体现）
- 助力规则 CRUD 为物理删除（无 status/软删除），删除后小程序面板立即不再展示该条。
- 任务码（task_code）硬编码在 DefaultTasks，后台不可新增任务类型；只能编辑已有任务的文案参数，不能创造新任务。
- 「禁止助力」开关语义反转：checked=true → enable_assist=false → 后端拒绝助力（2180）。前端默认不勾选=助力开启。
- daily_browse 的 15 秒停留由小程序前端计时后上报，后端不校验停留时长；理论上可被绕过，设计上接受此风险。
- 翻倍窗口时长在代码内固定，无后台配置入口；调整需改 assist.go 常量。

====================================================================
4. 小程序端操作
====================================================================

助力面板为抽奖详情页内嵌组件 pages/lottery/detail/assist-panel/：
- 展示条件：cfg.enable_assist && 已登录 && 已参与（joined）——此时普通参与区被「点我助力」大圆按钮 + 进度点 + 限时翻倍倒计时替代；右侧滑出触发条与详情页右滑>40px 手势同门槛（未开助力时均不出现/不响应）。
- 数据：GET /mp/lottery/:id/assist-status（助力值/倍数/翻倍截止时间）+ GET /mp/lottery/:id/assist-tasks（任务列表与完成态）。
- 做任务：
  - watch_video：createRewardedVideoAd 激励视频，看完 POST /mp/lottery/:id/assist
  - 有 link_path 的任务（如 daily_browse）：写 app.globalData.pendingAssistTask → wx.switchTab 跳承载页（首页）→ 停留 15 秒后自动上报 doAssist
  - 心愿兑换：面板内直接触发，积分不足提示（2181）
- 参与者页 pages/lottery/participants/index：Tab 1=助力值排行榜，Tab 2=全部参与者。

====================================================================
5. API 速查
====================================================================

GET  /api/mp/lottery/:id/assist-status          我的助力状态（值/倍数/翻倍窗口）
GET  /api/mp/lottery/:id/assist-tasks           任务列表与完成进度
POST /api/mp/lottery/:id/assist                 完成任务 {task_code}（幂等；2180/2181 见上）
GET  /api/mp/lottery/:id/participants/leaderboard  助力榜
GET  /api/mp/config/assist-rules                助力规则文案（公开读）
GET/POST/PUT/DELETE /api/admin/config/assist-rules  规则 CRUD（config:read/write）

====================================================================
6. 注意事项
====================================================================

- 合规：任何新增助力相关文案/页面都不得出现「提升中奖率/中奖概率」表述（开奖为均匀洗牌，助力值不进权重，接权重需改开奖数学，刻意不做）。
- 翻倍窗口时长无配置入口；调整需改代码。
- assist.done 事件无消费者；接入新逻辑时注意别重复扣/加分。
- 排查「做了任务没加助力」：先查是否触发唯一键幂等（当日已做过 daily 任务）、是否 2180（活动未开助力，检查 cfg.enable_assist 与创建表单「禁止助力」是否被勾选）、daily_browse 是否真的在承载页停留满 15 秒后上报。
