diff --git a/docs/superpowers/plans/2026-04-07-wechat-privacy-userinfo.md b/docs/superpowers/plans/2026-04-07-wechat-privacy-userinfo.md new file mode 100644 index 0000000..208619f --- /dev/null +++ b/docs/superpowers/plans/2026-04-07-wechat-privacy-userinfo.md @@ -0,0 +1,473 @@ +# 微信隐私授权与用户信息功能实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 接入微信隐私授权 API,进入首页时检查隐私授权状态;分享时获取用户头像昵称并上传服务端 + +**Architecture:** +- WxSDK 工具类新增隐私授权和用户信息获取方法 +- StorageManager 新增用户信息本地缓存 +- PageHome 接入隐私授权检查 +- PageWriteLevels 接入用户信息获取与上传 +- ApiConfig 新增用户信息 API 端点 + +**Tech Stack:** Cocos Creator 3.8.8, TypeScript, 微信小游戏 API + +--- + +## 文件变更概览 + +| 文件 | 变更类型 | +|------|----------| +| `assets/scripts/config/ApiConfig.ts` | 新增端点 | +| `assets/scripts/utils/StorageManager.ts` | 新增用户信息存取方法 | +| `assets/scripts/utils/WxSDK.ts` | 新增隐私授权、用户信息获取方法 | +| `assets/prefabs/PageHome.ts` | 接入隐私授权检查 | +| `assets/prefabs/PageWriteLevels.ts` | 接入用户信息获取与上传 | + +--- + +## Task 1: ApiConfig 新增用户信息 API 端点 + +**Files:** +- Modify: `assets/scripts/config/ApiConfig.ts` + +- [ ] **Step 1: 添加用户信息 API 端点** + +打开文件 `assets/scripts/config/ApiConfig.ts`,在 `API_ENDPOINTS` 对象中添加: + +```typescript +USER_INFO: `${API_BASE}/user/info`, +``` + +最终文件应如下: + +```typescript +export const API_ENDPOINTS = { + WX_LOGIN: `${API_BASE}/auth/wx-login`, + USER_ASSETS: `${API_BASE}/user/assets`, + USER_ASSETS_CONSUME: `${API_BASE}/user/assets/consume`, + USER_ASSETS_EARN: `${API_BASE}/user/assets/earn`, + USER_GAME_DATA: `${API_BASE}/user/game-data`, + LEVELS: `${API_BASE}/wechat-game/levels`, + SHARE_CREATE: `${API_BASE}/share`, + USER_INFO: `${API_BASE}/user/info`, +} as const; +``` + +- [ ] **Step 2: 提交代码** + +```bash +git add assets/scripts/config/ApiConfig.ts +git commit -m "feat: 新增用户信息 API 端点" +``` + +--- + +## Task 2: StorageManager 新增用户信息缓存方法 + +**Files:** +- Modify: `assets/scripts/utils/StorageManager.ts` + +- [ ] **Step 1: 添加用户信息存储相关常量** + +打开 `assets/scripts/utils/StorageManager.ts`,在文件顶部找到存储 key 定义的位置,添加: + +```typescript +const STORAGE_KEYS = { + // ... 现有 keys + USER_INFO: 'user_info', +} as const; +``` + +- [ ] **Step 2: 新增用户信息存取方法** + +在 StorageManager 类中添加以下方法: + +```typescript +/** + * 保存用户信息(头像、昵称) + * @param userInfo 用户信息对象 { avatarUrl: string, nickName: string } + */ +static setUserInfo(userInfo: { avatarUrl: string; nickName: string }): void { + localStorage.setItem(STORAGE_KEYS.USER_INFO, JSON.stringify(userInfo)); +} + +/** + * 获取本地缓存的用户信息 + * @returns 用户信息对象或 null + */ +static getUserInfo(): { avatarUrl: string; nickName: string } | null { + const data = localStorage.getItem(STORAGE_KEYS.USER_INFO); + if (!data) return null; + try { + return JSON.parse(data); + } catch { + return null; + } +} + +/** + * 清除用户信息缓存 + */ +static clearUserInfo(): void { + localStorage.removeItem(STORAGE_KEYS.USER_INFO); +} +``` + +- [ ] **Step 3: 提交代码** + +```bash +git add assets/scripts/utils/StorageManager.ts +git commit -m "feat: StorageManager 新增用户信息缓存方法" +``` + +--- + +## Task 3: WxSDK 新增隐私授权和用户信息获取方法 + +**Files:** +- Modify: `assets/scripts/utils/WxSDK.ts` + +- [ ] **Step 1: 添加隐私授权检查方法** + +在 WxSDK 类末尾(`}` 之前)添加: + +```typescript +// ==================== 隐私授权相关 ==================== + +/** + * 检查用户是否已授权隐私 + * @returns Promise<{ needAuthorization: boolean }> needAuthorization 为 false 表示已授权 + */ +static checkPrivacySetting(): Promise<{ needAuthorization: boolean }> { + return new Promise((resolve, reject) => { + const wxApi = WxSDK.getWx(); + if (!wxApi) { + // 非微信环境,认为已授权 + resolve({ needAuthorization: false }); + return; + } + + if (typeof wxApi.getPrivacySetting !== 'function') { + // 低版本微信,认为已授权 + console.warn('[WxSDK] 当前微信版本不支持 getPrivacySetting'); + resolve({ needAuthorization: false }); + return; + } + + wxApi.getPrivacySetting({ + success: (res: any) => { + console.log('[WxSDK] 隐私授权检查结果:', res); + resolve({ needAuthorization: res.needAuthorization }); + }, + fail: (err: any) => { + console.warn('[WxSDK] 隐私授权检查失败:', err); + // 检查失败时认为需要授权 + resolve({ needAuthorization: true }); + } + }); + }); +} + +/** + * 引导用户进行隐私授权 + * 调用后会弹出微信隐私授权弹窗 + * @returns Promise + */ +static requirePrivacyAuthorize(): Promise { + return new Promise((resolve, reject) => { + const wxApi = WxSDK.getWx(); + if (!wxApi) { + console.warn('[WxSDK] 非微信环境,跳过隐私授权'); + resolve(); + return; + } + + if (typeof wxApi.requirePrivacyAuthorize !== 'function') { + console.warn('[WxSDK] 当前微信版本不支持 requirePrivacyAuthorize'); + resolve(); + return; + } + + wxApi.requirePrivacyAuthorize({ + success: () => { + console.log('[WxSDK] 用户已授权隐私'); + resolve(); + }, + fail: (err: any) => { + console.warn('[WxSDK] 用户拒绝或授权失败:', err); + reject(new Error(err.errMsg || '隐私授权失败')); + } + }); + }); +} +``` + +- [ ] **Step 2: 添加获取用户头像昵称方法** + +在同一文件中继续添加: + +```typescript +// ==================== 用户信息相关 ==================== + +/** + * 用户信息(头像、昵称) + */ +export interface WxUserInfo { + avatarUrl: string; + nickName: string; + country?: string; + province?: string; + city?: string; + gender?: number; +} + +/** + * 获取用户头像和昵称(需要用户主动授权) + * @returns Promise + */ +static getUserProfile(): Promise { + return new Promise((resolve, reject) => { + const wxApi = WxSDK.getWx(); + if (!wxApi) { + // 非微信环境,返回 mock 数据 + console.warn('[WxSDK] 非微信环境,返回 mock 用户信息'); + resolve({ + avatarUrl: '', + nickName: '微信用户' + }); + return; + } + + if (typeof wxApi.getUserProfile !== 'function') { + console.warn('[WxSDK] 当前微信版本不支持 getUserProfile'); + reject(new Error('当前微信版本不支持 getUserProfile')); + return; + } + + wxApi.getUserProfile({ + desc: '用于完善用户资料', + success: (res: any) => { + if (res.userInfo) { + console.log('[WxSDK] 获取用户信息成功'); + resolve({ + avatarUrl: res.userInfo.avatarUrl, + nickName: res.userInfo.nickName, + country: res.userInfo.country, + province: res.userInfo.province, + city: res.userInfo.city, + gender: res.userInfo.gender + }); + } else { + console.error('[WxSDK] 获取用户信息失败: 无 userInfo'); + reject(new Error('获取用户信息失败')); + } + }, + fail: (err: any) => { + console.error('[WxSDK] 获取用户信息失败:', err); + reject(new Error(err.errMsg || '获取用户信息失败')); + } + }); + }); +} +``` + +- [ ] **Step 3: 提交代码** + +```bash +git add assets/scripts/utils/WxSDK.ts +git commit -m "feat: WxSDK 新增隐私授权和用户信息获取方法" +``` + +--- + +## Task 4: PageHome 接入隐私授权检查 + +**Files:** +- Modify: `assets/prefabs/PageHome.ts` + +- [ ] **Step 1: 在 onViewLoad 中添加隐私授权检查** + +打开 `assets/prefabs/PageHome.ts`,在 `onViewLoad` 方法末尾添加: + +```typescript +/** + * 检查隐私授权状态 + */ +private async _checkPrivacyAuthorization(): Promise { + if (!WxSDK.isWechat()) { + console.log('[PageHome] 非微信环境,跳过隐私授权检查'); + return; + } + + try { + const { needAuthorization } = await WxSDK.checkPrivacySetting(); + if (needAuthorization) { + console.log('[PageHome] 用户未授权隐私,引导授权'); + await WxSDK.requirePrivacyAuthorize(); + } else { + console.log('[PageHome] 用户已授权隐私'); + } + } catch (err) { + console.warn('[PageHome] 隐私授权检查异常:', err); + } +} +``` + +- [ ] **Step 2: 在 onViewLoad 中调用隐私授权检查** + +在 `onViewLoad` 方法末尾添加调用: + +```typescript +onViewLoad(): void { + console.log('[PageHome] onViewLoad'); + this._initButtons(); + this._initWxShare(); + // 新增:检查隐私授权 + this._checkPrivacyAuthorization(); +} +``` + +- [ ] **Step 3: 提交代码** + +```bash +git add assets/prefabs/PageHome.ts +git commit -m "feat: PageHome 接入隐私授权检查" +``` + +--- + +## Task 5: PageWriteLevels 接入用户信息获取与上传 + +**Files:** +- Modify: `assets/prefabs/PageWriteLevels.ts` + +- [ ] **Step 1: 添加 HttpUtil import(如果还没有)** + +检查文件顶部 import,如果没有 HttpUtil,添加: + +```typescript +import { HttpUtil } from 'db://assets/scripts/utils/HttpUtil'; +``` + +- [ ] **Step 2: 在 _onCompleteClick 中添加用户信息获取逻辑** + +修改 `_onCompleteClick` 方法,在分享成功后的逻辑中添加用户信息获取: + +```typescript +private async _onCompleteClick(): Promise { + if (!this._validateSelection()) return; + + const shareTitle = this.shareTitleEditBox?.getComponent(EditBox)?.string?.trim() || ''; + if (!shareTitle) { + ToastManager.instance.show('请输入分享标题'); + return; + } + + if (this._isSubmitting) return; + this._isSubmitting = true; + + try { + const levelIds = this._getSelectedLevelIds(); + if (levelIds.length !== MAX_SELECTION) { + ToastManager.instance.show('获取关卡数据失败,请重试'); + return; + } + + const shareCode = await ShareManager.instance.createShare(shareTitle, levelIds); + if (!shareCode) { + ToastManager.instance.show('创建分享失败,请重试'); + return; + } + + console.log('[PageWriteLevels] 创建分享成功, code:', shareCode); + + // 获取用户头像昵称并上传 + await this._uploadUserInfo(); + + ShareManager.instance.triggerWxShare(shareTitle, shareCode); + ToastManager.instance.show('分享创建成功!'); + } catch (err) { + console.error('[PageWriteLevels] 完成按钮异常:', err); + ToastManager.instance.show('操作失败,请重试'); + } finally { + this._isSubmitting = false; + } +} +``` + +- [ ] **Step 3: 添加 _uploadUserInfo 方法** + +在 PageWriteLevels 类中添加新方法: + +```typescript +/** + * 获取用户头像昵称并上传到服务端 + */ +private async _uploadUserInfo(): Promise { + // 先检查本地缓存 + const cachedUserInfo = StorageManager.getUserInfo(); + if (cachedUserInfo) { + console.log('[PageWriteLevels] 使用缓存的用户信息'); + return; + } + + if (!WxSDK.isWechat()) { + console.log('[PageWriteLevels] 非微信环境,跳过获取用户信息'); + return; + } + + try { + const userInfo = await WxSDK.getUserProfile(); + + // 本地缓存 + StorageManager.setUserInfo(userInfo); + + // 上传到服务端 + const response = await HttpUtil.post( + API_ENDPOINTS.USER_INFO, + { + avatarUrl: userInfo.avatarUrl, + nickName: userInfo.nickName + }, + API_TIMEOUT.DEFAULT + ); + + if (response.success) { + console.log('[PageWriteLevels] 用户信息上传成功'); + } else { + console.warn('[PageWriteLevels] 用户信息上传失败:', response.message); + } + } catch (err) { + console.warn('[PageWriteLevels] 获取用户信息失败:', err); + // 不阻断主流程 + } +} +``` + +- [ ] **Step 4: 添加必要的 import** + +检查文件顶部 import,添加: + +```typescript +import { StorageManager } from 'db://assets/scripts/utils/StorageManager'; +import { API_ENDPOINTS, API_TIMEOUT } from 'db://assets/scripts/config/ApiConfig'; +``` + +- [ ] **Step 5: 提交代码** + +```bash +git add assets/prefabs/PageWriteLevels.ts +git commit -m "feat: PageWriteLevels 接入用户信息获取与上传" +``` + +--- + +## 实现完成 + +所有任务已完成,实现功能: +1. ✅ PageHome 进入时检查隐私授权状态,未授权则引导用户授权 +2. ✅ PageWriteLevels 分享时获取用户头像昵称,本地缓存并上传服务端 +3. ✅ 新增用户信息 API 端点 +4. ✅ StorageManager 支持用户信息缓存 \ No newline at end of file diff --git a/docs/superpowers/specs/2026-04-07-wechat-privacy-userinfo-design.md b/docs/superpowers/specs/2026-04-07-wechat-privacy-userinfo-design.md new file mode 100644 index 0000000..6f5ab9b --- /dev/null +++ b/docs/superpowers/specs/2026-04-07-wechat-privacy-userinfo-design.md @@ -0,0 +1,97 @@ +# 微信隐私授权与用户信息功能设计 + +## 概述 + +接入微信小游戏隐私授权 API,实现: +1. 进入 PageHome 时检查用户是否已授权隐私,未授权则引导授权 +2. PageWriteLevels 点击分享按钮时获取用户头像、昵称并上传服务端 + +## 功能设计 + +### 1. 隐私授权检查 + +**触发时机**: PageHome `onViewLoad` + +**实现逻辑**: +```typescript +// 检查用户是否已授权隐私 +wx.getPrivacySetting({ + success: (res) => { + if (!res.needAuthorization) { + // 已授权,继续正常流程 + return; + } + // 未授权,调用引导授权 + wx.requirePrivacyAuthorize({ + success: () => { + console.log('用户已授权隐私'); + }, + fail: () => { + console.log('用户拒绝授权'); + } + }); + } +}); +``` + +### 2. 用户头像昵称获取 + +**触发时机**: PageWriteLevels 点击分享按钮 (`_onCompleteClick`) + +**实现逻辑**: +1. 调用 `wx.getUserProfile` 获取用户头像和昵称 +2. 上传到服务端 +3. 本地 StorageManager 缓存 + +```typescript +wx.getUserProfile({ + desc: '用于完善用户资料', + success: async (res) => { + const userInfo = res.userInfo; + // 上传服务端 + await HttpUtil.post(API_ENDPOINTS.USER_INFO_UPDATE, { + avatarUrl: userInfo.avatarUrl, + nickName: userInfo.nickName + }); + // 本地缓存 + StorageManager.setUserInfo(userInfo); + }, + fail: (err) => { + console.error('获取用户信息失败', err); + } +}); +``` + +### 3. 新增 API 端点 + +在 `ApiConfig.ts` 中添加: + +| 端点 | 方法 | 用途 | +|------|------|------| +| `/user/info` | POST | 更新用户头像昵称 | +| `/user/info` | GET | 获取用户头像昵称 | + +### 4. 数据存储 + +**本地 StorageManager**: +- `userInfo` - 用户头像昵称缓存 + +**服务端**: +- 新增用户信息表,存储头像 URL 和昵称 + +## 文件变更 + +| 文件 | 变更类型 | +|------|----------| +| `assets/scripts/config/ApiConfig.ts` | 新增端点 | +| `assets/scripts/utils/StorageManager.ts` | 新增用户信息存取方法 | +| `assets/scripts/utils/WxSDK.ts` | 新增隐私授权、用户信息获取方法 | +| `assets/scripts/utils/HttpUtil.ts` | 新增用户信息 API 调用方法 | +| `assets/prefabs/PageHome.ts` | 接入隐私授权检查 | +| `assets/prefabs/PageWriteLevels.ts` | 接入用户信息获取与上传 | + +## 错误处理 + +- 隐私授权失败:提示用户"需要授权隐私才能使用完整功能",但不阻断流程 +- 获取用户信息失败:提示用户"获取头像昵称失败",继续原有流程 +- 服务端上传失败:本地缓存成功,服务端下次登录时同步 \ No newline at end of file