# 06 签到抽奖（心愿换红包，for AI Agents）

> 关联代码：internal/service/signin_lottery/（含 scheduler.go）、internal/model/signin_lottery.go
> 管理端页面：web/src/views/signin-lottery/；小程序页面：pages/signin-lottery/detail/
> 深度设计文档：docs/signin-lottery/design.md 等

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

签到抽奖（SigninLottery）：连续签到 N 天（资格天数 1-7 可配）达标即获得开奖资格，到点自动开奖，奖品常为现金红包（「心愿换红包」营销概念）。与普通抽奖完全独立的子系统（独立模型/Service/错误码段 2150-2159），但共用 drawcore 批量开奖算法与红包钱包入账（见 10）。

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

2.1 模型
- SigninLottery：活动。状态机 draft → active → (drawn | cancelled)；cancelled 可恢复为 draft 再激活。字段含活动期、资格天数（1-7）、每日签到积分、开奖时间、富文本详情。
- SigninRecord：签到记录（用户×活动×日期）。
- 无独立奖品表：奖池经 PrizeDTO（含 Amount 红包金额）随活动存储；开奖写 Winner（LotteryType=signin_lottery），红包金额写在 Winner.Extra{redpacket_amount_fen}（签到抽奖无 model.Prize 行，红包入账按 Extra 解析金额）。

2.2 开奖
- Scheduler 每 30 秒扫描：active 且到达开奖时间 → 自动开奖。
- 引擎复用 drawcore.BatchDraw：候选人为「资格达标用户 ID 列表」（连签满 N 天），奖品名额展开 + 洗牌配对。
- 开奖抢占同样用条件更新防并发重复开奖。

2.3 红包入账
- 开奖后 afterDrawCreditRedpackets：按 Winner.Extra 金额入账钱包（credit_mode=auto 时；claim 模式用户领取触发），幂等键防重。
- 签到本身每日发积分（每日签到积分字段），积分体系见 13。

2.4 错误码段 2150-2159
- 2152 今日已签到；其余为活动状态/资格类（sentinel 在 service/signin_lottery 包）。

====================================================================
3. 后台操作（抽奖管理 → 签到抽奖 /signin-lottery）
====================================================================

3.1 列表（views/signin-lottery/List.vue）
- 筛选：搜索抽奖（关键词）、状态。
- 表格列：ID/标题/报名开始时间/报名结束时间/开奖时间/参与人数/状态/操作。
- 行操作：「详情」；「恢复草稿」（cancelled → draft）；「激活」（draft → active）；「取消」；「开奖」（到达开奖时间后才可用，手动触发）；「删除」。
- 「新建活动」→ /signin-lottery/new。

3.2 详情/编辑页 /signin-lottery/:id（Tab 结构）
- 基本信息：标题、封面、简介、富文本详情（wangeditor）、活动期（daterange）、资格天数（1-7）、每日签到积分、开奖时间 + PrizeEditor 奖池编辑（名称/类型/数量/红包金额/图片/简介/发放方式）。
- 签到记录（/:id/records 独立路由）：汇总统计 + 分页表。
- 资格名单：内联表，按用户签到天数判定是否达标。
- 中奖名单（/:id/winners 独立路由）。
- 指定中奖：FixedWinnerPanel（支持 signin_lottery 业务）。

3.3 后台操作逻辑备注（隐藏约束，UI 不直接体现）
- 单例激活：激活一个签到抽奖活动时，系统自动取消（cancelled）所有其他当前 active 的签到抽奖活动——同一时刻只允许一个进行中的签到活动。操作前确认影响范围。
- 删除约束：仅 draft/cancelled 状态可删除；删除活动时级联删除所有关联的 SigninRecord（签到记录不可恢复）。
- 状态流转：draft→active（激活，同时取消其他 active）；active→drawn（自动开奖后）或 active→cancelled（手动取消）；cancelled→draft（恢复草稿，可重新编辑后再激活）。
- 恢复草稿影响：cancelled 恢复为 draft 后重新激活会重新开始资格判定周期，已签到的用户连签记录虽然保留但需要在新活动期内重新达标。

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

pages/signin-lottery/detail/index（「心愿换红包」落地页，首页心愿卡片入口）：
- 展示 7 日签到日历（已签/漏签状态）、当前连签进度 vs 资格天数、开奖倒计时。
- 「签到」按钮 → POST /api/mp/signin-lottery/:id/checkin（每日一次，2152 已签）。
- 达标后等待自动开奖；开奖后展示中奖名单/我的中奖。
- 分享：GET /:id/share 取分享素材。
- 现金红包到账后走钱包/提现（见 10）。

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

小程序端（/api/mp）：
GET  /api/mp/signin-lottery/active         当前进行中的活动（首页入口用）
GET  /api/mp/signin-lottery/:id            详情
POST /api/mp/signin-lottery/:id/checkin    每日签到
GET  /api/mp/signin-lottery/:id/qualified  资格名单/我的资格
GET  /api/mp/signin-lottery/:id/share      分享素材

管理端（/api/admin，权限 signin_lottery:*）：
GET|POST|PUT|DELETE /api/admin/signin-lotteries(:id)   CRUD
PUT  /api/admin/signin-lotteries/:id/status            状态流转（激活/取消/恢复草稿）
POST /api/admin/signin-lotteries/:id/draw              手动开奖
GET  /api/admin/signin-lotteries/:id/records|qualified|winners

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

- 资格判定以「连续签到天数」为准（不是累计天数）；漏签一天断连，具体口径以 service 实现为准，运营解释口径前先核对代码。
- 不支持测试抽奖（IsTest 仅普通抽奖有）；也不支持即抽即中/组团/阶段开奖。
- 红包金额在 Winner.Extra，不在 Prize 表——排查「红包没到账」时先查 Extra 里的 redpacket_amount_fen 与 credit_mode 配置。
- cancelled 恢复 draft 后重新激活会重新开始资格判定周期，操作前评估对已签到用户的影响。
