Files
mp-xieyingeng/.claude/plan.md
2026-04-05 13:37:58 +08:00

7.5 KiB

Implementation Plan: User Auth + Server-Side Lives Management

Current State Analysis

Client (Cocos Creator)

  • Currency: "Lives" (生命值), new user starts with 10
  • Earning: +1 life per correct answer (PageLevel.showSuccess()addLife())
  • Spending: -1 life per hint unlock (hints 2 & 3 only, PageLevel.onUnlockClue()consumeLife())
  • Storage: All local via StorageManager using sys.localStorage
  • No auth: WxSDK only handles sharing/vibration, no wx.login
  • HttpUtil: Has GET/POST, but no auth headers

Server (NestJS)

  • Read-only: 4 GET endpoints (configs + levels)
  • No auth, no user system, no guards
  • Uses repository pattern consistently

Phase 1: Server - Auth Module (WeChat Login)

1.1 Install dependencies

cd MemeMind-Server
pnpm add @nestjs/jwt axios

1.2 New files to create

Entity: src/modules/auth/entities/user.entity.ts

  • id (UUID, PK)
  • openid (varchar 128, unique index)
  • sessionKey (varchar 255, nullable) - WeChat session_key
  • nickname (varchar 100, nullable)
  • avatarUrl (text, nullable)
  • lives (int, default 10) - 生命值/积分
  • createdAt, updatedAt

DTO: src/modules/auth/dto/wx-login.dto.ts

  • WxLoginRequestDto - { code: string }
  • WxLoginResponseDto - { token: string, user: { id, nickname, lives } }

Repository: src/modules/auth/repositories/user.repository.ts

  • findByOpenid(openid), findById(id), create(data), save(user)

Service: src/modules/auth/auth.service.ts

  • wxLogin(code): Call WeChat API https://api.weixin.qq.com/sns/jscode2session with appid/secret + code → get openid/session_key → find or create user → sign JWT → return token + user info

Guard: src/common/guards/jwt-auth.guard.ts

  • Custom Guard: extract Bearer token from header → verify JWT → attach user to request

Controller: src/modules/auth/auth.controller.ts

  • POST /v1/auth/wx-login - public endpoint, accepts { code }, returns { token, user }

Module: src/modules/auth/auth.module.ts

  • Imports: JwtModule, TypeOrmModule.forFeature([User])
  • Exports: JwtModule (so other modules can use JwtService)

1.3 Environment variables

Add to .env and env.validation.ts:

  • WX_APPID - 微信小程序 AppID
  • WX_SECRET - 微信小程序 AppSecret
  • JWT_SECRET - JWT signing secret

Phase 2: Server - User Assets API (Lives Management)

2.1 New files

DTO: src/modules/auth/dto/user-assets.dto.ts

  • UserAssetsResponseDto - { lives: number }
  • ConsumeLifeRequestDto - { reason: 'hint_unlock', levelId?: string, hintIndex?: number }
  • EarnLifeRequestDto - { reason: 'level_complete', levelId: string }

Endpoints added to auth controller (or new user controller):

  • GET /v1/user/assets - [Auth Required] Get current lives
  • POST /v1/user/assets/consume - [Auth Required] Consume 1 life (for hint unlock)
  • POST /v1/user/assets/earn - [Auth Required] Earn 1 life (for level completion)

2.2 Business logic safety

  • Consume: Check lives > 0 before deducting, return error if insufficient
  • Earn: Server validates the reason, +1 life
  • Idempotency consideration: For level_complete, track completed levels per user to prevent duplicate rewards

2.3 New Entity: src/modules/auth/entities/user-level-progress.entity.ts

  • id (UUID, PK)
  • userId (varchar, FK → User)
  • levelId (varchar, FK → Level)
  • completedAt (datetime)
  • Unique index on (userId, levelId) - prevent duplicate completion rewards

Phase 3: Server - Protect Existing Endpoints + Loading Data API

3.1 Composite loading endpoint

GET /v1/user/game-data - [Auth Required] Returns everything needed at loading:

{
  "user": { "id": "...", "lives": 10 },
  "levels": [ ... ],  // reuse existing level data
  "progress": { "completedLevelIds": ["level-1", "level-2"] }
}

This replaces the client making multiple API calls during loading.

3.2 Auth on existing endpoints

Keep /v1/wechat-game/levels and /v1/wechat-game/configs as public (no auth needed for level data). New user-specific endpoints require auth.


Phase 4: Client - WeChat Login Integration

4.1 WxSDK - Add login method

static login(): Promise<string>  // returns wx code

Calls wx.login() → returns code

4.2 New file: assets/scripts/utils/AuthManager.ts

Singleton managing auth state:

  • login(): WxSDK.login() → POST /v1/auth/wx-login → store token + user data
  • getToken(): return cached token
  • getUserLives(): return cached lives
  • isLoggedIn(): boolean
  • Store token in localStorage

4.3 HttpUtil - Add auth support

  • Add setAuthToken(token) static method
  • Modify GET/POST to attach Authorization: Bearer <token> header when token exists

Phase 5: Client - Connect Lives to Server

5.1 New file: assets/scripts/utils/UserAssetsManager.ts

Singleton managing user assets (lives) with server sync:

  • fetchAssets(): GET /v1/user/assets → update local lives
  • consumeLife(reason, levelId?, hintIndex?): POST /v1/user/assets/consume → update local
  • earnLife(reason, levelId): POST /v1/user/assets/earn → update local
  • Falls back to local StorageManager if network fails

5.2 PageLoading - Updated flow

start()
  → WxSDK.login() get code
  → POST /auth/wx-login → get token + user data (including lives)
  → Store token, sync lives to StorageManager
  → GET /levels (existing, now with auth optional)
  → Preload assets
  → Open PageHome

5.3 PageLevel - Updated logic

  • onUnlockClue(): Call UserAssetsManager.consumeLife('hint_unlock', levelId, hintIndex) instead of StorageManager.consumeLife()
  • showSuccess()nextLevel(): Call UserAssetsManager.earnLife('level_complete', levelId) instead of StorageManager.addLife()
  • Keep StorageManager as local cache/fallback

File Change Summary

Server - New Files (10 files)

  1. src/modules/auth/auth.module.ts
  2. src/modules/auth/auth.controller.ts
  3. src/modules/auth/auth.service.ts
  4. src/modules/auth/entities/user.entity.ts
  5. src/modules/auth/entities/user-level-progress.entity.ts
  6. src/modules/auth/repositories/user.repository.ts
  7. src/modules/auth/repositories/user-level-progress.repository.ts
  8. src/modules/auth/dto/wx-login.dto.ts
  9. src/modules/auth/dto/user-assets.dto.ts
  10. src/common/guards/jwt-auth.guard.ts

Server - Modified Files (3 files)

  1. src/app.module.ts - Import AuthModule
  2. src/config/env.validation.ts - Add WX_APPID, WX_SECRET, JWT_SECRET
  3. src/main.ts - Add Bearer auth to Swagger config

Client - New Files (2 files)

  1. assets/scripts/utils/AuthManager.ts
  2. assets/scripts/utils/UserAssetsManager.ts

Client - Modified Files (4 files)

  1. assets/scripts/utils/WxSDK.ts - Add login() method
  2. assets/scripts/utils/HttpUtil.ts - Add auth token support
  3. assets/PageLoading.ts - Add login flow before loading
  4. assets/prefabs/PageLevel.ts - Use UserAssetsManager for earn/consume

API Endpoints Summary

Method Endpoint Auth Description
POST /v1/auth/wx-login No WeChat code → JWT token
GET /v1/user/assets Yes Get user lives
POST /v1/user/assets/consume Yes Consume 1 life (hint)
POST /v1/user/assets/earn Yes Earn 1 life (level complete)
GET /v1/user/game-data Yes Loading composite endpoint
GET /v1/wechat-game/levels No Existing, stays public
GET /v1/wechat-game/configs No Existing, stays public