|
@@ -0,0 +1,277 @@
|
|
|
|
|
+# mini-program-promotion - Work Plan
|
|
|
|
|
+
|
|
|
|
|
+## TL;DR (For humans)
|
|
|
|
|
+<!-- Fill this LAST, after the detailed plan below is written, so it summarizes the REAL plan. -->
|
|
|
|
|
+<!-- Plain English for a non-engineer: NO file paths, NO todo numbers, NO wave/agent/tool names. -->
|
|
|
|
|
+
|
|
|
|
|
+**What you'll get:** 小程序的推广功能全面升级——完成3个"即将上线"占位页(分享邀请卡、推广团队、微信转发),新增6个增长功能(新手任务奖励、每日任务、优惠券、邀请里程碑、推广排行榜),外加后台管理页面。目标:拉新(分享卡片+排行榜+里程碑)→ 激活(新手任务)→ 留存(每日任务)→ 变现(优惠券)。
|
|
|
|
|
+
|
|
|
|
|
+**Why this approach:** AARRR增长框架(拉新→激活→留存→变现→裂变),按优先级分3层:先补齐已有的半成品(P0,投资回报率最高),再补核心增长缺口(P1,激活+变现基石),最后加强裂变(P2,利用现有两级佣金体系)。优先完成"即将上线"占位页,因为它们已经有用户期待但不可用——修复后立刻产生价值。
|
|
|
|
|
+
|
|
|
|
|
+**What it will NOT do:** 不实现推送通知(需微信订阅消息)、不实现免费试用会员(需调整支付流程)、不实现拼团/限时抢购(需并发处理)、不实现百分比优惠券(只做固定金额)、不实现三级以上团队树。
|
|
|
|
|
+
|
|
|
|
|
+**Effort:** Large — 19个任务项,5个工作波次,覆盖后端+小程序+管理端
|
|
|
|
|
+**Risk:** Medium — 优惠券需集成现有支付流程,涉及金额计算;其他功能独立,风险低
|
|
|
|
|
+**Decisions I made for you:**
|
|
|
|
|
+- 分享邀请卡 → Canvas本地海报+保存相册(非服务端生成图片,无需额外基础设施)
|
|
|
|
|
+- 团队页 → 只展示L1/L2成员列表(非树形可视化,两级佣金模型已够用)
|
|
|
|
|
+- 邀请里程碑 → 3/5/10人三档,积分+能量奖励(200/500/1000点——金额约2-10元,有竞争力但不滥用)
|
|
|
|
|
+- 排行榜 → 每周重置,显示前20名(每周防固化,20名足够激励)
|
|
|
|
|
+- 每日任务 → 4个:签到/完成任务/分享/AI对话(覆盖已有功能,驱动DAU)
|
|
|
|
|
+- 优惠券 → 仅固定金额(最简实现,无复杂边界情况)
|
|
|
|
|
+- 触发方式 → 直接服务层调用,非AOP/事件(与现有代码风格一致,简单可调试)
|
|
|
|
|
+- 新手任务的"已完成" → 仅统计上线后的行为,不追溯历史数据
|
|
|
|
|
+
|
|
|
|
|
+Your next move: 审阅并批准(approve)本计划,然后我运行自动高精度评审(双Momus)。批准仅授权编写计划,不授权执行。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+> TL;DR (machine): <1 line - effort, risk, deliverables>
|
|
|
|
|
+
|
|
|
|
|
+## Scope
|
|
|
|
|
+### Must have
|
|
|
|
|
+- **C1 Share Card**: Canvas-based poster with user avatar + referral code + mini-program QR code + brand imagery, save to album
|
|
|
|
|
+- **C2 Team Page**: L1/L2 member list with pagination (nickname, avatar, join date, order count, commission earned)
|
|
|
|
|
+- **C3 WeChat Share**: onShareAppMessage wired on promotion/index, promotion/invite, parent/invite with referral code in path
|
|
|
|
|
+- **C4 Onboarding Rewards**: 6 newbie tasks (REGISTER, COMPLETE_PROFILE, ADD_CHILD, FIRST_CHECKIN, FIRST_TASK, BIND_REFERRAL) with points+energy rewards
|
|
|
|
|
+- **C5 Growth Tasks**: 4 daily tasks (checkin, complete task, share article, AI chat) + 3 newbie tasks (same as C4 subset); DB-configurable
|
|
|
|
|
+- **C6 Coupon System**: Fixed-amount coupons (coupon + user_coupon tables), claim + apply at checkout, admin CRUD
|
|
|
|
|
+- **C7 Invite Milestones**: 3 tiers (3/5/10 invites → 200/500/1000 points + energy), checked on bindReferral
|
|
|
|
|
+- **C8 Referral Leaderboard**: Weekly top 20, reset Monday, ranked by referral count
|
|
|
|
|
+- **Admin management**: Web admin pages for coupons, growth tasks, milestones, leaderboard view
|
|
|
|
|
+
|
|
|
|
|
+### Must NOT have (guardrails, anti-slop, scope boundaries)
|
|
|
|
|
+- NO re-engagement push notifications (requires WeChat subscribe message infrastructure not yet in place)
|
|
|
|
|
+- NO trial/free membership (requires payment flow changes beyond scope)
|
|
|
|
|
+- NO group buy / flash sale / time-limited promotion engine (requires order concurrency handling)
|
|
|
|
|
+- NO percentage-based coupons (fixed-amount only; percentage adds boundary-case complexity)
|
|
|
|
|
+- NO team tree visualization beyond L1/L2 (marginal UX value)
|
|
|
|
|
+- NO server-side poster image generation (Canvas poster is sufficient for mini-program)
|
|
|
|
|
+- NO AOP / event-driven architecture for task triggers (direct service calls match existing codebase style)
|
|
|
|
|
+- NO changes to web/ static site or cfc-web/ admin router (backend + frontend only)
|
|
|
|
|
+- NO retroactive credit for tasks completed before onboarding system launch
|
|
|
|
|
+
|
|
|
|
|
+## Verification strategy
|
|
|
|
|
+> Zero human intervention - all verification is agent-executed.
|
|
|
|
|
+- Test decision: tests-after (existing codebase has no test harness for controller/service layers; integration tests via curl against running server)
|
|
|
|
|
+- Framework: bash curl for API verification, uni-app dev build for frontend page existence checks
|
|
|
|
|
+- Evidence: .omo/evidence/task-<N>-mini-program-promotion.<ext>
|
|
|
|
|
+- Every new backend endpoint: curl happy-path + error-path, assert Result code=0 for success, code!=0 for validation failure
|
|
|
|
|
+- Every new frontend page: verify page registers in pages.json and component renders without JS error
|
|
|
|
|
+
|
|
|
|
|
+## Execution strategy
|
|
|
|
|
+### Parallel execution waves
|
|
|
|
|
+- **Wave 1 (P0 — Complete Stubs)**: C1 Share Card, C2 Team Page, C3 WeChat Share — these are independent, can be parallelized
|
|
|
|
|
+- **Wave 2 (P1 — Onboarding)**: C4 Onboarding Rewards backend + frontend — depends on Wave 1 for share-card component reuse pattern
|
|
|
|
|
+- **Wave 3 (P1 — Growth + Coupon)**: C5 Growth Tasks, C6 Coupon System — independent of each other, can be parallelized
|
|
|
|
|
+- **Wave 4 (P2 — Referral Amplification)**: C7 Invite Milestones, C8 Leaderboard — C7 depends on C4 (onboarding defines reward pattern); C8 independent
|
|
|
|
|
+- **Wave 5 (Admin + Integration)**: Admin pages for all new features + end-to-end integration testing — depends on all prior waves
|
|
|
|
|
+
|
|
|
|
|
+### Dependency matrix
|
|
|
|
|
+| Todo | Depends on | Blocks | Can parallelize with |
|
|
|
|
|
+| --- | --- | --- | --- |
|
|
|
|
|
+| 1 (Share Card) | — | 3 (share card image for onShareAppMessage) | 2, 4 |
|
|
|
|
|
+| 2 (Team API) | — | 5 (Team frontend) | 1, 3 |
|
|
|
|
|
+| 3 (WeChat Share) | 1 (share poster for imageUrl) | — | 2, 4 |
|
|
|
|
|
+| 4 (Team Frontend) | 2 (Team API) | — | 1, 3 |
|
|
|
|
|
+| 5 (Onboarding DB+API) | — | 6 (Onboarding frontend) | 1, 2, 3 |
|
|
|
|
|
+| 6 (Onboarding Frontend) | 5 | — | 7, 8 |
|
|
|
|
|
+| 7 (Growth Tasks API) | — | 9 (Growth Tasks frontend) | 5, 8 |
|
|
|
|
|
+| 8 (Coupon API) | — | 10 (Coupon frontend) | 5, 7 |
|
|
|
|
|
+| 9 (Growth Tasks Frontend) | 7 | — | 6, 10 |
|
|
|
|
|
+| 10 (Coupon Frontend) | 8 | — | 6, 9 |
|
|
|
|
|
+| 11 (Coupon Checkout Integration) | 8, 10 | — | 9 |
|
|
|
|
|
+| 12 (Invite Milestones API) | — | 13 (Milestones frontend) | 7, 8 |
|
|
|
|
|
+| 13 (Invite Milestones Frontend) | 12 | — | 9, 10 |
|
|
|
|
|
+| 14 (Leaderboard API) | — | 15 (Leaderboard frontend) | 12, 13 |
|
|
|
|
|
+| 15 (Leaderboard Frontend) | 14 | — | 13 |
|
|
|
|
|
+| 16 (Admin Coupons) | 8 | — | 14, 15 |
|
|
|
|
|
+| 17 (Admin Growth Tasks) | 7 | — | 16, 15 |
|
|
|
|
|
+| 18 (Admin Milestones+Leaderboard) | 12, 14 | — | 16, 17 |
|
|
|
|
|
+| 19 (E2E Integration) | All above | — | — |
|
|
|
|
|
+
|
|
|
|
|
+## Todos
|
|
|
|
|
+> Implementation + Test = ONE todo. Never separate.
|
|
|
|
|
+<!-- APPEND TASK BATCHES BELOW THIS LINE WITH edit/apply_patch - never rewrite the headers above. -->
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 1. Share Card — Canvas poster component + backend mini-program QR endpoint
|
|
|
|
|
+ What to do / Must NOT do: Create a `SharePoster` component in cfc-frontend that renders a Canvas poster with: user avatar (or default icon), nickname, referral code in large text, mini-program QR code (from WechatService.generateWxacode with scene=referralCode), brand imagery (brand orange gradient background, "浠艾福" logo text). On tap of "分享邀请卡" button in promotion/index.vue, show poster modal, then "保存到相册" button that calls `uni.canvasToTempFilePath` → `uni.saveImageToPhotosAlbum`. Handle album permission denial with `uni.openSetting`. Replace the stub `shareCard()` at line 131. Also wire the same in promotion/invite.vue at line 143. Must NOT generate server-side images or use wx.shareAppMessage for poster sharing (save-to-album is the standard pattern).
|
|
|
|
|
+ Parallelization: Wave 1 | Blocked by: none | Blocks: 3 (needs poster for share imageUrl)
|
|
|
|
|
+ References: cfc-frontend/pages/promotion/index.vue:1-285 (full page, stub at line 131), cfc-frontend/pages/promotion/invite.vue:1-335 (stub at line 143), cfc-backend/src/main/java/com/etotem/cfc/service/WechatService.java (generateWxacode method), cfc-backend/src/main/java/com/etotem/cfc/service/QrCodeService.java (Base64 QR generation), cfc-backend/src/main/java/com/etotem/cfc/controller/InviteController.java (getReferralCode endpoint), cfc-frontend/utils/api.js (getReferralCode function), uni-app Canvas API docs: https://uniapp.dcloud.net.cn/api/canvas/createCanvasContext.html
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `grep -c 'canvasToTempFilePath' cfc-frontend/pages/promotion/index.vue` returns ≥1; (2) `grep -c 'saveImageToPhotosAlbum' cfc-frontend/pages/promotion/index.vue` returns ≥1; (3) `grep '即将上线' cfc-frontend/pages/promotion/index.vue` returns 0 (stub removed); (4) same 3 checks for promotion/invite.vue; (5) `grep -c 'SharePoster\\|share-poster\\|sharePoster' cfc-frontend/pages/promotion/index.vue` or component file returns ≥1
|
|
|
|
|
+ QA scenarios: happy: user taps "分享邀请卡" → Canvas renders poster → taps "保存" → image saved to album → toast "已保存"; failure: user denies album permission → modal prompts "需要相册权限" → button opens settings page via uni.openSetting. Evidence: .omo/evidence/task-1-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(promotion): add Canvas share poster with mini-program QR code
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 2. Team Page — Backend API for L1/L2 member list with pagination
|
|
|
|
|
+ What to do / Must NOT do: Add `POST /api/commission/team/list` endpoint to CommissionController. Accepts: page (int, default 1), size (int, default 20), level (String, "L1" or "L2" or "ALL" default). Returns paginated list of referred users with: id, nickname, avatarUrl, createdAt (join date), orderCount (count of commission records where this user is buyer), commissionEarned (sum of commission where referrer is current user and buyer is this member). Add `getTeamList(userId, level, page, size)` method to CommissionService. SQL: JOIN user table on buyer_id = referred_user's user_id, LEFT JOIN commission_record for order count and commission sum. Must handle: users with null nickname (show "微信用户"), users with null avatar (show default icon). Must NOT show deeper tree beyond L1/L2.
|
|
|
|
|
+ Parallelization: Wave 1 | Blocked by: none | Blocks: 4 (Team frontend)
|
|
|
|
|
+ References: cfc-backend/src/main/java/com/etotem/cfc/controller/CommissionController.java (existing team endpoint at /api/commission/team), cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java (getTeamStats method), cfc-backend/src/main/java/com/etotem/cfc/entity/CommissionRecord.java (buyerId, referrerId, commissionType, level fields), cfc-backend/src/main/java/com/etotem/cfc/mapper/CommissionRecordMapper.java, cfc-backend/src/main/java/com/etotem/cfc/dto/CommissionSummaryDTO.java (for field naming convention)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `grep -c 'team/list' cfc-backend/src/main/java/com/etotem/cfc/controller/CommissionController.java` returns ≥1; (2) `grep 'getTeamList' cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java` returns match; (3) `mvn compile -pl cfc-backend -q` exits 0 (no compile errors); (4) endpoint returns Result with paginated records when called with valid JWT
|
|
|
|
|
+ QA scenarios: happy: GET /api/commission/team/list?page=1&size=20 returns Result(code=0, data={records:[...], total:N}); failure: no JWT → 401; no referred users → returns empty list with total=0. Evidence: .omo/evidence/task-2-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(commission): add paginated team member list endpoint
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 3. WeChat Share — Wire onShareAppMessage on promotion pages
|
|
|
|
|
+ What to do / Must NOT do: Add `onShareAppMessage()` lifecycle hook to 3 pages: (1) cfc-frontend/pages/promotion/index.vue, (2) cfc-frontend/pages/promotion/invite.vue, (3) cfc-frontend/pages/parent/invite/index.vue. Each returns: title="我在浠艾福发现了好用的家庭成长工具,快来加入!" or similar, path="/pages/invite/join?inviteCode=REFERRAL_CODE" (where REFERRAL_CODE is the user's referral code from data), imageUrl=share poster image (default brand image if poster not generated yet). The landing page pages/invite/join.vue already exists and accepts inviteCode. Must NOT use wx.shareTimeline (moments sharing requires separate review). Must NOT change the path structure of existing pages.
|
|
|
|
|
+ Parallelization: Wave 1 | Blocked by: 1 (optional — uses poster image, but can use default imageUrl) | Blocks: none
|
|
|
|
|
+ References: cfc-frontend/pages/promotion/index.vue:1-285, cfc-frontend/pages/promotion/invite.vue:1-335, cfc-frontend/pages/parent/invite/index.vue, cfc-frontend/pages/invite/join.vue (existing landing page with invite code acceptance), uni-app onShareAppMessage docs: https://uniapp.dcloud.net.cn/api/plugins/share.html
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `grep -c 'onShareAppMessage' cfc-frontend/pages/promotion/index.vue` returns ≥1; (2) same for invite.vue; (3) same for parent/invite/index.vue; (4) each contains 'inviteCode' in the path string literal
|
|
|
|
|
+ QA scenarios: happy: user taps "..." → "转发" in WeChat → share card preview shows title+image → recipient opens → lands on invite/join page with inviteCode param; failure: user with no referral code → share still works, path omits inviteCode param. Evidence: .omo/evidence/task-3-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(share): wire onShareAppMessage for promotion and invite pages
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 4. Team Page — Frontend team members list page
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-frontend/pages/promotion/team.vue. Shows: (1) Team stats card at top (L1 count, L2 count, total team commission — reuse data from /api/commission/team), (2) Segmented control: "直推(L1)" / "间推(L2)" tabs, (3) Paginated member list: each item shows avatar (or default icon), nickname (or "微信用户"), join date, order count badge, commission earned. (4) Load more onReachBottom. Replace stub `goTeam()` in promotion/index.vue at line 142-143 with `uni.navigateTo({ url: '/pages/promotion/team' })`. Add page route to pages.json under promotion subpackage. Add API function `getTeamList(page, size, level)` to api.js. Must NOT show L3+ or tree visualization.
|
|
|
|
|
+ Parallelization: Wave 1 | Blocked by: 2 (Team API) | Blocks: none
|
|
|
|
|
+ References: cfc-frontend/pages/promotion/index.vue:142-143 (goTeam stub), cfc-frontend/pages/promotion/commission.vue:1-294 (similar list page pattern for reference), cfc-frontend/pages/promotion/invite.vue:1-335 (similar stats card pattern), cfc-frontend/pages.json (subpackages section for route registration), cfc-frontend/utils/api.js (add getTeamList function)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-frontend/pages/promotion/team.vue` exits 0; (2) `grep 'promotion/team' cfc-frontend/pages.json` returns match; (3) `grep 'getTeamList' cfc-frontend/utils/api.js` returns match; (4) `grep '即将上线' cfc-frontend/pages/promotion/index.vue` returns 0 (all stubs removed); (5) `grep 'promotion/team' cfc-frontend/pages/promotion/index.vue` returns match (navigation target)
|
|
|
|
|
+ QA scenarios: happy: user taps "推广团队" → navigates to team page → sees L1/L2 tabs → taps L2 → sees L2 members; failure: no team members → shows empty state "还没有推广团队成员"; API error → shows error toast. Evidence: .omo/evidence/task-4-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(promotion): add team members list page with L1/L2 tabs
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 5. Onboarding Rewards — Backend: entity + table + service + controller
|
|
|
|
|
+ What to do / Must NOT do: Create (1) Entity: `OnboardingTask.java` with fields: id, userId, taskType(VARCHAR(32): REGISTER/COMPLETE_PROFILE/ADD_CHILD/FIRST_CHECKIN/FIRST_TASK/BIND_REFERRAL), rewardPoints(INT default 0), rewardEnergy(INT default 0), status(VARCHAR(16): PENDING/CLAIMED, default PENDING), completedAt(DATETIME), createdAt. (2) Mapper: `OnboardingTaskMapper.java`. (3) Service: `OnboardingService.java` with methods: initForUser(userId) — creates 6 PENDING tasks; completeTask(userId, taskType) — marks task completed, returns reward info; claimReward(userId, taskType) — sets status=CLAIMED, calls PointsService.awardSystemPoints + EnergyService.awardEnergy; getProgress(userId) — returns all tasks with status; auto-trigger: call completeTask from existing services — UserService (register → REGISTER), AuthController (profile update → COMPLETE_PROFILE), ChildService (addChild → ADD_CHILD), HealthCheckinService (checkin → FIRST_CHECKIN), TaskService (task completion → FIRST_TASK), BindInviteService/CommissionService (bindReferral → BIND_REFERRAL). (4) Controller: `OnboardingController.java` at /api/onboarding with endpoints: POST /api/onboarding/progress (get user's tasks), POST /api/onboarding/claim (claim reward for a task). (5) DatabaseInitializer: add CREATE TABLE IF NOT EXISTS onboarding_task + seed default reward values (REGISTER=100pts+10energy, COMPLETE_PROFILE=50pts+5energy, ADD_CHILD=100pts+10energy, FIRST_CHECKIN=30pts+5energy, FIRST_TASK=50pts+5energy, BIND_REFERRAL=50pts+10energy). Must NOT credit retroactively for tasks completed before system launch (only tasks completed after the code deploy count). Must NOT use AOP/event system.
|
|
|
|
|
+ Parallelization: Wave 2 | Blocked by: none | Blocks: 6 (Onboarding frontend)
|
|
|
|
|
+ References: cfc-backend/src/main/java/com/etotem/cfc/entity/PointsLog.java (entity pattern), cfc-backend/src/main/java/com/etotem/cfc/service/PointsService.java (awardSystemPoints method), cfc-backend/src/main/java/com/etotem/cfc/service/EnergyService.java (awardEnergy method), cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java (table creation pattern), cfc-backend/src/main/java/com/etotem/cfc/controller/InviteController.java (controller pattern with @PostMapping), cfc-backend/src/main/java/com/etotem/cfc/common/Result.java (response wrapper), cfc-backend/src/main/java/com/etotem/cfc/service/UserService.java (register method), cfc-backend/src/main/java/com/etotem/cfc/service/ChildService.java (addChild), cfc-backend/src/main/java/com/etotem/cfc/service/HealthCheckinService.java (checkin), cfc-backend/src/main/java/com/etotem/cfc/service/TaskService.java (completeTask), cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java (bindReferral)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-backend/src/main/java/com/etotem/cfc/entity/OnboardingTask.java` exits 0; (2) `grep 'class OnboardingService' cfc-backend/src/main/java/com/etotem/cfc/service/OnboardingService.java` returns match; (3) `grep 'class OnboardingController' cfc-backend/src/main/java/com/etotem/cfc/controller/OnboardingController.java` returns match; (4) `grep 'completeTask' cfc-backend/src/main/java/com/etotem/cfc/service/OnboardingService.java` returns match; (5) `mvn compile -pl cfc-backend -q` exits 0; (6) curl /api/onboarding/progress with valid JWT returns Result(code=0, data=[...tasks...])
|
|
|
|
|
+ QA scenarios: happy: new user registers → REGISTER task auto-completes → user calls /api/onboarding/progress → sees 6 tasks, REGISTER status=COMPLETED → calls /api/onboarding/claim with taskType=REGISTER → gets 100 points + 10 energy → status becomes CLAIMED; failure: claim already-claimed task → returns error "已领取"; claim non-existent task → returns error. Evidence: .omo/evidence/task-5-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(onboarding): add newbie task reward system with 6 task types
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 6. Onboarding Rewards — Frontend: progress page + claim UX
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-frontend/pages/profile/onboarding.vue. Shows: (1) Title "新手任务" with progress bar (X/6 completed), (2) Task list: each item shows emoji icon, task description, reward amount (points+energy), status badge (待完成/已完成/已领取), claim button (only when COMPLETED and not CLAIMED). On claim, call API, show success animation, update item status. Referral from: add "新手任务" entry in ProfileMenu.vue or profile.vue. Add route to pages.json. Add API functions: getOnboardingProgress, claimOnboardingReward to api.js. Must NOT show retroactive credit for pre-launch actions. Must NOT use optional chaining.
|
|
|
|
|
+ Parallelization: Wave 2 | Blocked by: 5 (Onboarding API) | Blocks: none
|
|
|
|
|
+ References: cfc-frontend/pages/profile/profile.vue (hub page for adding entry), cfc-frontend/pages/profile/components/ProfileMenu.vue (menu items), cfc-frontend/pages/rewards/rewards.vue (similar list page pattern), cfc-frontend/pages.json (route registration), cfc-frontend/utils/api.js (add API functions)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-frontend/pages/profile/onboarding.vue` exits 0; (2) `grep 'onboarding' cfc-frontend/pages.json` returns match; (3) `grep 'getOnboardingProgress\\|claimOnboardingReward' cfc-frontend/utils/api.js` returns match; (4) page contains progress bar and task list items
|
|
|
|
|
+ QA scenarios: happy: user taps "新手任务" → sees 6 tasks → completed ones show "领取" button → taps → gets toast "获得100积分+10能量"; failure: network error → shows error toast; all tasks already claimed → shows "全部完成" badge. Evidence: .omo/evidence/task-6-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(onboarding): add newbie task progress and claim page
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 7. Growth Tasks — Backend: entity + table + service + controller + daily reset
|
|
|
|
|
+ What to do / Must NOT do: Create (1) Entity: `GrowthTask.java` — id, type(VARCHAR: DAILY/NEWBIE), title, description, rewardPoints(INT), rewardEnergy(INT), targetValue(INT default 1, e.g. "complete 1 task"), taskKey(VARCHAR(32) unique: DAILY_CHECKIN/DAILY_TASK/DAILY_SHARE/DAILY_AI/NEWBIE_PROFILE/NEWBIE_CHILD/NEWBIE_REFERRAL), enabled(TINYINT default 1), sortOrder(INT). (2) Entity: `GrowthTaskLog.java` — id, userId, taskId, progress(INT default 0), completed(TINYINT default 0), claimed(TINYINT default 0), date(VARCHAR(10) for daily tasks, format YYYY-MM-DD), createdAt. (3) Mapper for both. (4) Service: `GrowthTaskService.java` — getTaskList(userId, type): returns tasks + user's progress for today (daily) or all-time (newbie); updateProgress(userId, taskKey, increment): updates progress, auto-marks completed when progress>=targetValue; claimReward(userId, taskLogId): validates task is completed and not claimed, then calls PointsService.awardSystemPoints + EnergyService.awardEnergy, sets claimed=1. (5) Auto-triggers: checkin → DAILY_CHECKIN, taskService.completeTask → DAILY_TASK, shareArticle → DAILY_SHARE (called from frontend onShareAppMessage success callback), AI chat message → DAILY_AI (called from chat page after sending message). (6) Scheduled task: `GrowthTaskDailyResetTask.java` — at midnight CST, no-op (daily tasks naturally reset by date field — each day's log is a new row). (7) Controller: `GrowthTaskController.java` at /api/growth-task with: POST /list (get tasks by type), POST /claim (claim reward). (8) DatabaseInitializer: CREATE TABLE growth_task + growth_task_log + seed 7 task rows. Default daily tasks: DAILY_CHECKIN(20pts+3energy), DAILY_TASK(30pts+5energy), DAILY_SHARE(10pts+2energy), DAILY_AI(15pts+3energy). Must NOT use AOP; must NOT allow claiming same task twice per day; must validate taskKey format (uppercase + underscore only, max 32 chars).
|
|
|
|
|
+ Parallelization: Wave 3 | Blocked by: none | Blocks: 9 (Growth Tasks frontend)
|
|
|
|
|
+ References: cfc-backend/src/main/java/com/etotem/cfc/entity/PointsLog.java (entity pattern), cfc-backend/src/main/java/com/etotem/cfc/service/PointsService.java (awardSystemPoints), cfc-backend/src/main/java/com/etotem/cfc/service/EnergyService.java (awardEnergy with daily caps: body=50,mind=40,wisdom=80,action=60,wealth=50,global=200), cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java (table creation + seed data), cfc-backend/src/main/java/com/etotem/cfc/task/StreakResetTask.java (scheduled task pattern), cfc-backend/src/main/java/com/etotem/cfc/service/HealthCheckinService.java (checkin trigger point), cfc-backend/src/main/java/com/etotem/cfc/service/TaskService.java (task completion trigger)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-backend/src/main/java/com/etotem/cfc/entity/GrowthTask.java` exits 0; (2) `test -f cfc-backend/src/main/java/com/etotem/cfc/entity/GrowthTaskLog.java` exits 0; (3) `grep 'class GrowthTaskService' cfc-backend/src/main/java/com/etotem/cfc/service/GrowthTaskService.java` returns match; (4) `grep 'class GrowthTaskController' cfc-backend/src/main/java/com/etotem/cfc/controller/GrowthTaskController.java` returns match; (5) `mvn compile -pl cfc-backend -q` exits 0; (6) seeded task count in growth_task table = 7
|
|
|
|
|
+ QA scenarios: happy: user checks in → DAILY_CHECKIN progress auto-incremented → auto-completes → user claims 20pts+3energy → claimed=1; second claim attempt → returns error "已领取"; failure: claim incomplete task → error; claim yesterday's task via today's log → not possible (date field prevents it). Evidence: .omo/evidence/task-7-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(growth-task): add daily and newbie growth task system with points+energy rewards
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 8. Coupon System — Backend: entity + table + service + controller
|
|
|
|
|
+ What to do / Must NOT do: Create (1) Entity: `Coupon.java` — id, name(VARCHAR(64)), type(VARCHAR(16): FIXED), value(INT in cents, e.g. 1000=10元), minSpend(INT in cents, 0=no minimum), applicableTo(VARCHAR(32): ALL/MEMBERSHIP/PRODUCT/ASSESSMENT, default ALL), validFrom(DATETIME), validUntil(DATETIME), totalCount(INT, issuer limit), usedCount(INT default 0), createdAt. (2) Entity: `UserCoupon.java` — id, userId, couponId, status(VARCHAR(16): AVAILABLE/USED/EXPIRED), receivedAt(DATETIME), usedAt(DATETIME), orderId(BIGINT nullable). (3) Mapper for both. (4) Service: `CouponService.java` — listAvailable(userId): returns user's AVAILABLE coupons; claim(userId, couponId): validates coupon not exhausted (usedCount<totalCount), not expired, user hasn't claimed before → creates UserCoupon record; apply(userId, couponId, orderType, orderAmount): validates coupon is AVAILABLE, applicableTo matches orderType, orderAmount>=minSpend, not expired → returns discount amount in cents, does NOT deduct yet (deduction happens at payment confirmation); markUsed(userCouponId, orderId): sets status=USED, usedAt=now, orderId. (5) Controller: `CouponController.java` at /api/coupon with: POST /list (user's coupons), POST /claim (claim a coupon by couponId), POST /apply (preview discount for an order). (6) Admin controller: `AdminCouponController.java` at /api/admin/coupon with: POST /create, POST /update, POST /list, POST /issue (batch issue to users). (7) DatabaseInitializer: CREATE TABLE coupon + user_coupon + seed 2 demo coupons (新用户专享10元券, minSpend=0, applicableTo=ALL; 会员升级50元券, minSpend=9900, applicableTo=MEMBERSHIP). Must NOT implement percentage coupons; must NOT allow double coupon on same order (one coupon per order max); must validate coupon existence before claim.
|
|
|
|
|
+ Parallelization: Wave 3 | Blocked by: none | Blocks: 10 (Coupon frontend), 11 (Checkout integration)
|
|
|
|
|
+ References: cfc-backend/src/main/java/com/etotem/cfc/entity/PointsLog.java (entity pattern, INT cents for money), cfc-backend/src/main/java/com/etotem/cfc/controller/PackagePaymentController.java (checkout flow where coupon will integrate), cfc-backend/src/main/java/com/etotem/cfc/controller/MembershipController.java (membership upgrade where coupon will integrate), cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java (table creation + seed), cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminCommissionController.java (admin controller pattern)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-backend/src/main/java/com/etotem/cfc/entity/Coupon.java` exits 0; (2) `test -f cfc-backend/src/main/java/com/etotem/cfc/entity/UserCoupon.java` exits 0; (3) `grep 'class CouponService' cfc-backend/src/main/java/com/etotem/cfc/service/CouponService.java` returns match; (4) `grep 'class CouponController' cfc-backend/src/main/java/com/etotem/cfc/controller/CouponController.java` returns match; (5) `grep 'class AdminCouponController' cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminCouponController.java` returns match; (6) `mvn compile -pl cfc-backend -q` exits 0
|
|
|
|
|
+ QA scenarios: happy: admin creates coupon → user claims it → user applies to order (amount>=minSpend) → discount returned; failure: claim exhausted coupon → "已领完"; apply coupon to order below minSpend → "未达到使用门槛"; claim same coupon twice → "已领取"; apply expired coupon → "已过期". Evidence: .omo/evidence/task-8-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(coupon): add fixed-amount coupon system with claim, apply, and admin CRUD
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 9. Growth Tasks — Frontend: daily task page + newbie task tab
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-frontend/pages/tasks/daily-tasks.vue. Shows: (1) Segmented control: "每日任务" / "新手任务" tabs, (2) Daily tasks: 4 items with progress indicators (0/1), reward amounts, claim buttons, (3) Newbie tasks: reuse onboarding tasks from C4/C5 — show same data from /api/growth-task/list?type=NEWBIE, (4) Progress bar showing X/N completed for current tab. Add route to pages.json. Add API functions: getGrowthTaskList, claimGrowthTaskReward to api.js. Wire auto-triggers: onShareAppMessage callback → call updateProgress(DAILY_SHARE); AI chat message sent → call updateProgress(DAILY_AI). Profile menu entry: "每日任务" linking to daily-tasks.vue. Must NOT use optional chaining; must NOT show tasks for previous dates.
|
|
|
|
|
+ Parallelization: Wave 3 | Blocked by: 7 (Growth Tasks API) | Blocks: none
|
|
|
|
|
+ References: cfc-frontend/pages/tasks/tasks.vue (existing task page for reference), cfc-frontend/pages/profile/components/ProfileMenu.vue (menu entry), cfc-frontend/pages/ai/chat.vue (DAILY_AI trigger point), cfc-frontend/pages.json (route registration), cfc-frontend/utils/api.js (add API functions)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-frontend/pages/tasks/daily-tasks.vue` exits 0; (2) `grep 'daily-tasks' cfc-frontend/pages.json` returns match; (3) `grep 'getGrowthTaskList\\|claimGrowthTaskReward' cfc-frontend/utils/api.js` returns match; (4) page has segmented control with "每日任务" and "新手任务"
|
|
|
|
|
+ QA scenarios: happy: user opens daily tasks → sees 4 daily items → completed tasks show "领取" → taps → gets toast reward; failure: no tasks seeded → shows empty state; API error → shows error toast. Evidence: .omo/evidence/task-9-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(growth-task): add daily and newbie task page with claim UX
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 10. Coupon System — Frontend: coupon list + apply at checkout
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-frontend/pages/profile/coupons.vue. Shows: (1) Tab bar: "可使用" / "已使用" / "已过期", (2) Coupon cards: name, value (¥10), minSpend condition, valid period, applicable scope badge, (3) Empty state for each tab. Add route to pages.json. Add API functions: getCouponList, claimCoupon, applyCoupon to api.js. Profile menu entry: "我的优惠券". In checkout pages (shop/checkout.vue for packages, membership/index.vue for upgrade): add "选择优惠券" section → tap opens coupon selector popup → shows AVAILABLE coupons that match orderType and minSpend → select → shows discount amount → confirm → passes couponId in payment create request. Must NOT allow stacking multiple coupons per order.
|
|
|
|
|
+ Parallelization: Wave 3 | Blocked by: 8 (Coupon API) | Blocks: none
|
|
|
|
|
+ References: cfc-frontend/pages/profile/profile.vue (hub for entry), cfc-frontend/pages/profile/components/ProfileMenu.vue (menu), cfc-frontend/pages/shop/checkout.vue (checkout integration point for packages), cfc-frontend/pages/membership/index.vue (membership checkout integration), cfc-frontend/pages.json (route), cfc-frontend/utils/api.js (add API functions)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-frontend/pages/profile/coupons.vue` exits 0; (2) `grep 'profile/coupons' cfc-frontend/pages.json` returns match; (3) `grep 'getCouponList\\|claimCoupon\\|applyCoupon' cfc-frontend/utils/api.js` returns match; (4) `grep -c 'coupon' cfc-frontend/pages/shop/checkout.vue` returns ≥1 (checkout integration exists)
|
|
|
|
|
+ QA scenarios: happy: user goes to "我的优惠券" → sees available coupons → goes to checkout → selects coupon → discount shown → payment reduced; failure: no applicable coupons → shows "暂无可用优惠券"; coupon expired between selection and payment → API returns error "已过期". Evidence: .omo/evidence/task-10-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(coupon): add coupon list page and checkout integration
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 11. Coupon — Integrate coupon deduction in payment controllers
|
|
|
|
|
+ What to do / Must NOT do: Modify PackagePaymentController.createOrder and MembershipService.createOrder to accept optional `couponId` parameter. Logic: (1) validate coupon via CouponService.apply (checks available, applicableTo, minSpend, not expired), (2) calculate discounted amount = original price - coupon value (ensure discounted amount >= 0), (3) create order with discounted amount, (4) on payment success callback, call CouponService.markUsed to set coupon status=USED and link orderId. If order is cancelled/refunded, call CouponService to revert status back to AVAILABLE. Must NOT allow coupon value > order amount (cap at order amount). Must NOT skip the coupon's applicableTo check (a membership coupon cannot be used on a product order).
|
|
|
|
|
+ Parallelization: Wave 3 | Blocked by: 8 (Coupon API) | Blocks: none
|
|
|
|
|
+ References: cfc-backend/src/main/java/com/etotem/cfc/controller/PackagePaymentController.java (create order flow), cfc-backend/src/main/java/com/etotem/cfc/service/MembershipService.java (createOrder + processPaymentCallback methods), cfc-backend/src/main/java/com/etotem/cfc/service/CouponService.java (apply + markUsed methods from todo 8)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `grep 'couponId' cfc-backend/src/main/java/com/etotem/cfc/controller/PackagePaymentController.java` returns match; (2) `grep 'couponId' cfc-backend/src/main/java/com/etotem/cfc/service/MembershipService.java` returns match; (3) `grep 'markUsed' cfc-backend/src/main/java/com/etotem/cfc/service/CouponService.java` returns match or is called in payment callback flow; (4) `mvn compile -pl cfc-backend -q` exits 0
|
|
|
|
|
+ QA scenarios: happy: user creates order with valid coupon → order amount reduced → payment succeeds → coupon marked USED; failure: coupon not applicable to order type → "该优惠券不可用于此订单"; order amount < coupon minSpend → "未达到使用门槛"; payment fails → coupon stays AVAILABLE. Evidence: .omo/evidence/task-11-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(coupon): integrate coupon deduction in package and membership payment
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 12. Invite Milestones — Backend: entity + table + service + check-on-bind
|
|
|
|
|
+ What to do / Must NOT do: Create (1) Entity: `InviteMilestone.java` — id, userId, milestone(VARCHAR: INVITE_3/INVITE_5/INVITE_10), rewardPoints(INT), rewardEnergy(INT), status(VARCHAR: PENDING/CLAIMED), achievedAt(DATETIME), claimedAt(DATETIME). (2) Mapper. (3) Service: `InviteMilestoneService.java` — checkMilestones(userId): called from CommissionService.bindReferral after successful bind; counts total L1 referrals for userId from DB (SELECT COUNT from commission_record or user referral table); if count hits 3/5/10 and milestone not yet achieved → create InviteMilestone record with status=PENDING; claimReward(userId, milestoneKey): sets status=CLAIMED, awards points+energy; getMilestones(userId): returns all milestones with status. (4) Controller: `InviteMilestoneController.java` at /api/invite/milestone with: POST /list (user's milestones), POST /claim (claim a milestone reward). (5) DatabaseInitializer: CREATE TABLE invite_milestone + seed 3 milestone definitions (INVITE_3: 200pts+20energy, INVITE_5: 500pts+50energy, INVITE_10: 1000pts+100energy). Must use DB-level COUNT for referral count (not app-level increment) to prevent race conditions. Must NOT allow duplicate milestone claims.
|
|
|
|
|
+ Parallelization: Wave 4 | Blocked by: none | Blocks: 13 (Milestones frontend)
|
|
|
|
|
+ References: cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java (bindReferral method — hook point for checkMilestones call), cfc-backend/src/main/java/com/etotem/cfc/entity/CommissionRecord.java (to count referrals: referrerId=userId, level=L1), cfc-backend/src/main/java/com/etotem/cfc/service/PointsService.java (awardSystemPoints), cfc-backend/src/main/java/com/etotem/cfc/service/EnergyService.java (awardEnergy), cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java (table creation + seed)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-backend/src/main/java/com/etotem/cfc/entity/InviteMilestone.java` exits 0; (2) `grep 'class InviteMilestoneService' cfc-backend/src/main/java/com/etotem/cfc/service/InviteMilestoneService.java` returns match; (3) `grep 'checkMilestones' cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java` returns match (hook integrated); (4) `mvn compile -pl cfc-backend -q` exits 0
|
|
|
|
|
+ QA scenarios: happy: user's 3rd referral binds → checkMilestones called → INVITE_3 milestone created → user claims → 200pts+20energy awarded; failure: 2nd referral → no milestone triggered; claim already-claimed milestone → error; race condition (2 simultaneous binds) → DB COUNT=correct number, milestone created exactly once (INSERT IF NOT EXISTS guard). Evidence: .omo/evidence/task-12-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(invite-milestone): add invite milestone reward system with 3/5/10 tiers
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 13. Invite Milestones — Frontend: milestone display in promotion flow
|
|
|
|
|
+ What to do / Must NOT do: Add milestone progress section to promotion/invite.vue (below stats card). Shows: (1) "邀请里程碑" card with 3 progress bars (3/5/10), each showing currentInviteCount/target, (2) Achieved milestones show "领取" button, (3) Claimed milestones show checkmark. Add API functions: getInviteMilestones, claimInviteMilestone to api.js. Also add a milestone notification when a new milestone is achieved: show a modal/toast "恭喜达成邀请3人里程碑!去领取奖励" with navigation to promotion/invite. Must NOT use optional chaining.
|
|
|
|
|
+ Parallelization: Wave 4 | Blocked by: 12 (Milestones API) | Blocks: none
|
|
|
|
|
+ References: cfc-frontend/pages/promotion/invite.vue:1-335 (add milestone section after stats card), cfc-frontend/utils/api.js (add API functions), cfc-frontend/pages/promotion/index.vue (add milestone notification entry)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `grep 'inviteMilestone\\|invite-milestone' cfc-frontend/pages/promotion/invite.vue` returns match; (2) `grep 'getInviteMilestones\\|claimInviteMilestone' cfc-frontend/utils/api.js` returns match; (3) milestone section has progress indicators for 3, 5, 10 targets
|
|
|
|
|
+ QA scenarios: happy: user with 3+ referrals sees INVITE_3 milestone as "已达成" → taps "领取" → gets reward toast; failure: user with 0 referrals → milestones show "0/3" progress. Evidence: .omo/evidence/task-13-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(invite-milestone): add milestone progress and claim UI to promotion page
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 14. Referral Leaderboard — Backend: entity + table + service + weekly reset
|
|
|
|
|
+ What to do / Must NOT do: Create (1) Entity: `ReferralLeaderboard.java` — id, userId, weekStart(VARCHAR(10): Monday date in YYYY-MM-DD), referralCount(INT default 0), rank(INT nullable). (2) Mapper. (3) Service: `ReferralLeaderboardService.java` — incrementCount(userId): on successful bindReferral, increment this week's referralCount (INSERT ON DUPLICATE KEY UPDATE); getTopUsers(weekStart, limit=20): SELECT top 20 by referralCount DESC for given week with user nickname/avatar; getMyRank(userId, weekStart): returns user's rank and count; getCurrentWeek(): returns YYYY-MM-DD of current Monday. (4) Scheduled task: `LeaderboardWeeklyResetTask.java` — no explicit reset needed (each week is a new row keyed by weekStart). (5) Controller: `ReferralLeaderboardController.java` at /api/leaderboard with: POST /top (top 20), POST /my-rank (current user's rank). Hook: call incrementCount from CommissionService.bindReferral after successful bind. Use UTC+8 timezone for week calculation. Must NOT show all-time leaderboard (weekly only).
|
|
|
|
|
+ Parallelization: Wave 4 | Blocked by: none | Blocks: 15 (Leaderboard frontend)
|
|
|
|
|
+ References: cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java (bindReferral — hook point for incrementCount), cfc-backend/src/main/java/com/etotem/cfc/task/StreakResetTask.java (scheduled task pattern — though no explicit reset needed), cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java (table creation)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-backend/src/main/java/com/etotem/cfc/entity/ReferralLeaderboard.java` exits 0; (2) `grep 'class ReferralLeaderboardService' cfc-backend/src/main/java/com/etotem/cfc/service/ReferralLeaderboardService.java` returns match; (3) `grep 'incrementCount' cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java` returns match (hook integrated); (4) `mvn compile -pl cfc-backend -q` exits 0
|
|
|
|
|
+ QA scenarios: happy: user A refers 5 users this week → leaderboard shows A at #1 with count=5; user B refers 3 → B at #2; 0-referral user → not in top 20; failure: no referrals this week → top list empty but returns code=0 with empty array. Evidence: .omo/evidence/task-14-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(leaderboard): add weekly referral leaderboard with top 20 and personal rank
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 15. Referral Leaderboard — Frontend: leaderboard page
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-frontend/pages/promotion/leaderboard.vue. Shows: (1) Title "推广排行榜" with week label ("本周"), (2) Top 3 users with special styling (gold/silver/bronze), (3) Ranked list #4-#20 with avatar, nickname, referral count, (4) My rank card at bottom (sticky) showing current user's rank and count, (5) Empty state "本周还没有推广达人". Add route to pages.json. Add API functions: getLeaderboardTop, getLeaderboardMyRank to api.js. Add entry from promotion/index.vue menu (replace or beside existing items). Must NOT use optional chaining.
|
|
|
|
|
+ Parallelization: Wave 4 | Blocked by: 14 (Leaderboard API) | Blocks: none
|
|
|
|
|
+ References: cfc-frontend/pages/promotion/index.vue (add menu entry), cfc-frontend/pages.json (route), cfc-frontend/utils/api.js (add API functions)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-frontend/pages/promotion/leaderboard.vue` exits 0; (2) `grep 'promotion/leaderboard' cfc-frontend/pages.json` returns match; (3) `grep 'getLeaderboardTop\\|getLeaderboardMyRank' cfc-frontend/utils/api.js` returns match
|
|
|
|
|
+ QA scenarios: happy: user opens leaderboard → sees weekly top 20 → sees own rank at bottom → top 3 have special styling; failure: not in top 20 → "我的排名" shows "未上榜". Evidence: .omo/evidence/task-15-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(leaderboard): add weekly referral leaderboard page with top-3 styling
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 16. Admin — Coupon management page in cfc-web
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-web/src/views/admin/CouponManagement.vue. Shows: (1) Coupon list table with columns: name, value(元), minSpend(元), applicableTo, validFrom-validUntil, totalCount, usedCount, actions(edit/delete), (2) "新建优惠券" button → dialog form: name, value(cents→yuan input), minSpend, applicableTo(dropdown: ALL/MEMBERSHIP/PRODUCT/ASSESSMENT), validFrom(validUntil date range picker), totalCount, (3) "批量发放" button → select coupon + enter userIds → batch issue. Add route in cfc-web/src/router/index.js under admin children. Add sidebar menu entry in Layout.vue. Add API service functions in cfc-web/src/api/. Must NOT allow editing coupon value after users have claimed it (add validation).
|
|
|
|
|
+ Parallelization: Wave 5 | Blocked by: 8 (Coupon API) | Blocks: none
|
|
|
|
|
+ References: cfc-web/src/views/admin/ (existing admin pages for pattern), cfc-web/src/router/index.js (route registration), cfc-web/src/views/Layout.vue (sidebar menu), cfc-web/src/api/ (API service pattern)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-web/src/views/admin/CouponManagement.vue` exits 0; (2) `grep 'CouponManagement\\|coupon-management' cfc-web/src/router/index.js` returns match; (3) page has table and create dialog
|
|
|
|
|
+ QA scenarios: happy: admin creates coupon → appears in list → edits valid period → saves; issue batch → success; failure: create coupon with zero value → validation error; edit claimed coupon value → blocked. Evidence: .omo/evidence/task-16-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(admin): add coupon management page with CRUD and batch issue
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 17. Admin — Growth Task + Onboarding management page in cfc-web
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-web/src/views/admin/GrowthTaskManagement.vue. Two tabs: (1) "成长任务" tab — table of growth_task records, edit rewardPoints/rewardEnergy/enabled/sortOrder, create new daily/newbie tasks, (2) "新手任务" tab — table of onboarding task configs (taskType → rewardPoints mapping), edit reward amounts. Add route in router/index.js. Add sidebar entry. Must NOT allow deleting tasks that have user logs (soft-disable by setting enabled=0 instead).
|
|
|
|
|
+ Parallelization: Wave 5 | Blocked by: 5, 7 (Onboarding + Growth Task APIs) | Blocks: none
|
|
|
|
|
+ References: cfc-web/src/views/admin/ (existing admin pages), cfc-web/src/router/index.js, cfc-web/src/views/Layout.vue
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-web/src/views/admin/GrowthTaskManagement.vue` exits 0; (2) `grep 'GrowthTaskManagement\\|growth-task-management' cfc-web/src/router/index.js` returns match; (3) page has two tabs for growth tasks and onboarding configs
|
|
|
|
|
+ QA scenarios: happy: admin edits daily task reward points → saves → task updated; disables a task → enabled=0; failure: delete task with existing logs → blocked, must disable instead. Evidence: .omo/evidence/task-17-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(admin): add growth task and onboarding management page
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 18. Admin — Invite Milestone + Leaderboard management page in cfc-web
|
|
|
|
|
+ What to do / Must NOT do: Create cfc-web/src/views/admin/PromotionManagement.vue. Two tabs: (1) "邀请里程碑" — table of milestone definitions (milestone key, rewardPoints, rewardEnergy), edit reward amounts, (2) "排行榜" — view current week's leaderboard, view historical weeks, search by user. Add route in router/index.js. Add sidebar entry. Must NOT allow editing milestones that have already been achieved by users (only pending milestones editable).
|
|
|
|
|
+ Parallelization: Wave 5 | Blocked by: 12, 14 (Milestone + Leaderboard APIs) | Blocks: none
|
|
|
|
|
+ References: cfc-web/src/views/admin/ (existing admin pages), cfc-web/src/router/index.js, cfc-web/src/views/Layout.vue
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) `test -f cfc-web/src/views/admin/PromotionManagement.vue` exits 0; (2) `grep 'PromotionManagement\\|promotion-management' cfc-web/src/router/index.js` returns match; (3) page has two tabs for milestones and leaderboard
|
|
|
|
|
+ QA scenarios: happy: admin views current leaderboard → sees top 20; edits milestone reward → saves; failure: edit achieved milestone → blocked. Evidence: .omo/evidence/task-18-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | feat(admin): add invite milestone and leaderboard management page
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 19. End-to-end integration test — all promotion flows
|
|
|
|
|
+ What to do / Must NOT do: Write integration test scripts (bash + curl) that test the complete promotion flow end-to-end: (1) New user registers → onboarding tasks created, REGISTER auto-completed → user claims reward → points+energy credited, (2) User completes profile → COMPLETE_PROFILE auto-completed → claims → reward, (3) User adds child → ADD_CHILD → reward, (4) User checks in → FIRST_CHECKIN + DAILY_CHECKIN → rewards, (5) User gets referral code → shares → new user binds → INVITE milestone checked, leaderboard incremented, (6) User claims coupon → applies at checkout → discount applied, (7) User views team → sees L1 member, (8) User views leaderboard → sees own rank. Test all API endpoints with valid and invalid JWT, with missing parameters, with boundary values (0 amount, negative amount, overly long strings). Must NOT test against production database; use test-mode or separate schema.
|
|
|
|
|
+ Parallelization: Wave 5 | Blocked by: all prior todos | Blocks: none
|
|
|
|
|
+ References: All prior references; cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java; cfc-backend/src/main/resources/application.yml (test-mode config)
|
|
|
|
|
+ Acceptance criteria (agent-executable): (1) test script file exists and is executable; (2) running against a test instance returns 0 exit code; (3) all happy-path tests pass; (4) all failure-path tests return correct error codes
|
|
|
|
|
+ QA scenarios: happy: full flow from registration through referral reward passes; failure: any broken integration → test exits non-zero with error message. Evidence: .omo/evidence/task-19-mini-program-promotion.txt
|
|
|
|
|
+ Commit: Y | test(promotion): add end-to-end integration test for all promotion flows
|
|
|
|
|
+
|
|
|
|
|
+## Final verification wave
|
|
|
|
|
+> Runs in parallel after ALL todos. ALL must APPROVE. Surface results and wait for the user's explicit okay before declaring complete.
|
|
|
|
|
+- [ ] F1. Plan compliance audit — verify every todo is DONE (all acceptance criteria pass), no todos skipped
|
|
|
|
|
+- [ ] F2. Code quality review — verify no hardcoded secrets, no optional chaining in frontend, all @PostMapping, lint backend with `mvn compile`, lint frontend with `npm run lint:mp-weixin`
|
|
|
|
|
+- [ ] F3. Real manual QA — open the mini-program in WeChat devtools, walk through: share card flow, team page, coupon claim→checkout, onboarding rewards, leaderboard, milestone claim. Verify all stubs are replaced.
|
|
|
|
|
+- [ ] F4. Scope fidelity — verify all Must NOT have constraints are respected (no push notifications, no trial membership, no percentage coupons, no AOP, no web/ changes)
|
|
|
|
|
+
|
|
|
|
|
+## Commit strategy
|
|
|
|
|
+- Each todo = one atomic commit. 19 commits total.
|
|
|
|
|
+- Commit message format: `<type>(<scope>): <summary>`
|
|
|
|
|
+- types: `feat` (feature), `fix` (bugfix), `test` (tests), `refactor` (refactoring), `docs` (documentation)
|
|
|
|
|
+- scopes: `promotion` (frontend pages), `share` (share card), `commission` (team page), `onboarding` (newbie tasks), `growth-task` (daily tasks), `coupon` (coupons + checkout), `invite-milestone` (milestones), `leaderboard` (referral ranking), `admin` (management pages)
|
|
|
|
|
+- Branch: `cfclub` (current branch)
|
|
|
|
|
+- After all commits, push once and create a concise summary of changes
|
|
|
|
|
+
|
|
|
|
|
+## Success criteria
|
|
|
|
|
+- All 3 "即将上线" stubs replaced with working features: share card saves poster to album, team page shows L1/L2 members, WeChat share works
|
|
|
|
|
+- New user can complete 6 onboarding tasks and claim rewards (points + energy)
|
|
|
|
|
+- User can complete 4 daily tasks and claim rewards
|
|
|
|
|
+- User can claim fixed-amount coupons and apply them at checkout (packages + membership)
|
|
|
|
|
+- User can earn milestone rewards at 3/5/10 invitations
|
|
|
|
|
+- Weekly leaderboard shows top 20 referrers with personal rank
|
|
|
|
|
+- Admin can manage coupons (CRUD + batch issue), growth tasks (edit rewards), milestones (edit rewards), view leaderboard
|
|
|
|
|
+- All new backend endpoints return Result<T> with correct code/message/data
|
|
|
|
|
+- All new frontend pages register in pages.json and render without JS error
|
|
|
|
|
+- `mvn compile -pl cfc-backend -q` exits 0
|
|
|
|
|
+- No optional chaining (?.) in any new .vue file
|
|
|
|
|
+- No CSS Grid in any new .vue file
|
|
|
|
|
+- All new endpoints use @PostMapping
|