feat: 支持登录、个人信息存储
This commit is contained in:
204
.claude/plan.md
Normal file
204
.claude/plan.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# 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
|
||||
```bash
|
||||
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:
|
||||
```json
|
||||
{
|
||||
"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
|
||||
```typescript
|
||||
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 |
|
||||
Reference in New Issue
Block a user