Compare commits
3 Commits
4fb2a94b5b
...
7ea0406336
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ea0406336 | |||
| 371b478e33 | |||
| 7d6c4ea3c6 |
@@ -22,7 +22,18 @@
|
||||
"usedAt": 1775908159660,
|
||||
"industryId": "all"
|
||||
}
|
||||
],
|
||||
"f2494c1730eb411aac709ec2751d60fc": [
|
||||
{
|
||||
"expertId": "SeniorDeveloper",
|
||||
"name": "Will",
|
||||
"profession": "高级开发工程师",
|
||||
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
|
||||
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
|
||||
"usedAt": 1775921148885,
|
||||
"industryId": "all"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastUpdated": 1775909408190
|
||||
"lastUpdated": 1775921440281
|
||||
}
|
||||
44
.workbuddy/memory/2026-04-11.md
Normal file
44
.workbuddy/memory/2026-04-11.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# 2026-04-11 工作记录
|
||||
|
||||
## 任务:修复邀请加入应用流程
|
||||
|
||||
### 问题描述
|
||||
`passport/invite/index` 页面在新用户扫码加入应用时存在问题:
|
||||
- 新用户点击"微信手机号快速加入"时,如果未注册,后端返回"用户不存在"
|
||||
- 页面引导用户去登录,但登录完成后**没有自动执行加入应用的操作**
|
||||
|
||||
### 解决方案
|
||||
参考 `UserCard.tsx` 和 `phone-auth/index.tsx` 的实现模式,修改了 `src/passport/invite/index.tsx`:
|
||||
|
||||
#### 1. 新增功能
|
||||
- 页面加载时保存邀请 token 到本地存储
|
||||
- 检测用户登录状态
|
||||
- 未登录用户点击加入时,保存邀请信息并引导到 `phone-auth` 登录页
|
||||
- 登录成功后返回邀请页面,自动执行加入操作
|
||||
- 新增 `clearPendingInviteData` 方法清理临时数据
|
||||
|
||||
#### 2. 关键修改点
|
||||
- 使用 `saveInviteParams` 保存邀请信息
|
||||
- 使用 `pending_invite_*` 系列 storage key 保存待处理的邀请数据
|
||||
- 页面显示时通过 `AppShow` 事件监听登录返回
|
||||
- 登录成功后自动调用 `handleJoinApp` 完成加入
|
||||
|
||||
#### 3. 流程优化
|
||||
```
|
||||
新用户流程:
|
||||
1. 扫码进入邀请页面 → 保存 invite token
|
||||
2. 点击"微信手机号快速加入"
|
||||
3. 检测到未登录 → 保存 pending 数据 → 跳转到 phone-auth
|
||||
4. 完成登录/注册 → 返回邀请页面
|
||||
5. 自动检测 pending 数据 → 自动执行加入
|
||||
6. 加入成功 → 跳转到首页
|
||||
|
||||
已登录用户流程:
|
||||
1. 扫码进入邀请页面
|
||||
2. 点击"微信手机号快速加入"
|
||||
3. 直接执行加入操作
|
||||
4. 加入成功 → 跳转到首页
|
||||
```
|
||||
|
||||
### 文件修改
|
||||
- `src/passport/invite/index.tsx` - 完整重构邀请流程
|
||||
@@ -11,6 +11,13 @@
|
||||
"scene": null,
|
||||
"launchMode": "default"
|
||||
},
|
||||
{
|
||||
"name": "passport/invite/index",
|
||||
"pathName": "passport/invite/index",
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "passport/invite",
|
||||
"pathName": "passport/invite",
|
||||
|
||||
@@ -6,11 +6,20 @@ import Taro, { useRouter } from '@tarojs/taro';
|
||||
// 注意:使用 /api/_app 前缀表示小程序专用接口(免登录)
|
||||
const INVITE_API_URL = 'https://websopy-api.websoft.top';
|
||||
import { TenantId } from "@/config/app";
|
||||
import { getStoredInviteParams, saveInviteParams, clearInviteParams } from "@/utils/invite";
|
||||
|
||||
/**
|
||||
* 邀请加入确认页面
|
||||
*
|
||||
* 用户扫描邀请二维码后,打开此小程序页面确认加入应用
|
||||
*
|
||||
* 流程:
|
||||
* 1. 用户扫码进入页面,解析 token 参数
|
||||
* 2. 获取邀请信息展示给用户
|
||||
* 3. 用户点击"微信手机号快速加入"
|
||||
* 4. 如果用户未登录,保存邀请信息并引导到登录页
|
||||
* 5. 登录成功后返回此页面,自动执行加入操作
|
||||
* 6. 已登录用户直接执行加入操作
|
||||
*/
|
||||
|
||||
// 微信获取手机号回调参数类型
|
||||
@@ -45,8 +54,13 @@ const InvitePage: React.FC = () => {
|
||||
const [agreementChecked, setAgreementChecked] = useState(false);
|
||||
const [token, setToken] = useState<string>('');
|
||||
const [error, setError] = useState<string>('');
|
||||
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
// 检查用户是否已登录
|
||||
const accessToken = Taro.getStorageSync('access_token');
|
||||
setIsLoggedIn(!!accessToken);
|
||||
|
||||
// 从 URL 参数中获取 token
|
||||
const params = router.params;
|
||||
let inviteToken = params.scene || params.token || params.qrCodeKey || '';
|
||||
@@ -66,6 +80,12 @@ const InvitePage: React.FC = () => {
|
||||
|
||||
// 获取邀请信息
|
||||
if (inviteToken) {
|
||||
// 保存邀请 token 到本地存储,供登录后使用
|
||||
saveInviteParams({
|
||||
inviter: inviteToken,
|
||||
source: 'app_invite',
|
||||
t: Date.now().toString()
|
||||
});
|
||||
fetchInviteInfo(inviteToken);
|
||||
} else {
|
||||
setError('无效的邀请链接');
|
||||
@@ -73,6 +93,33 @@ const InvitePage: React.FC = () => {
|
||||
}
|
||||
}, [router.params]);
|
||||
|
||||
// 页面显示时检查是否需要自动执行加入操作(从登录页返回)
|
||||
useEffect(() => {
|
||||
const handleShow = () => {
|
||||
// 检查是否有 pending 的邀请数据且用户已登录
|
||||
const pendingToken = Taro.getStorageSync('pending_invite_token');
|
||||
const accessToken = Taro.getStorageSync('access_token');
|
||||
|
||||
if (pendingToken && accessToken) {
|
||||
console.log('检测到登录后返回,自动执行加入应用操作');
|
||||
// 更新登录状态
|
||||
setIsLoggedIn(true);
|
||||
// 自动执行加入操作
|
||||
handleJoinApp();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听页面显示事件
|
||||
Taro.eventCenter.on('AppShow', handleShow);
|
||||
|
||||
// 立即检查一次(处理页面首次加载时已经是登录状态的情况)
|
||||
handleShow();
|
||||
|
||||
return () => {
|
||||
Taro.eventCenter.off('AppShow', handleShow);
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 获取邀请信息
|
||||
*/
|
||||
@@ -110,6 +157,9 @@ const InvitePage: React.FC = () => {
|
||||
|
||||
/**
|
||||
* 处理微信手机号授权
|
||||
*
|
||||
* 如果用户未登录,先引导到登录页面完成注册/登录
|
||||
* 登录成功后返回此页面自动执行加入操作
|
||||
*/
|
||||
const handleGetPhoneNumber = async ({ detail }: GetPhoneNumberEvent) => {
|
||||
const { code, encryptedData, iv, errMsg } = detail;
|
||||
@@ -131,28 +181,57 @@ const InvitePage: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查用户是否已登录
|
||||
const accessToken = Taro.getStorageSync('access_token');
|
||||
|
||||
if (!accessToken) {
|
||||
// 未登录用户:保存当前邀请信息,引导到登录页面
|
||||
// 保存邀请 token 和微信授权信息,供登录后使用
|
||||
Taro.setStorageSync('pending_invite_token', token);
|
||||
Taro.setStorageSync('pending_invite_phone_code', code);
|
||||
if (encryptedData) Taro.setStorageSync('pending_invite_encrypted_data', encryptedData);
|
||||
if (iv) Taro.setStorageSync('pending_invite_iv', iv);
|
||||
|
||||
// 引导到手机号授权登录页面
|
||||
Taro.navigateTo({
|
||||
url: `/passport/phone-auth/index?redirect=${encodeURIComponent('/passport/invite/index?token=' + token)}`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 已登录用户:直接执行加入操作
|
||||
await handleJoinApp(code, encryptedData, iv);
|
||||
};
|
||||
|
||||
/**
|
||||
* 加入应用
|
||||
*
|
||||
* 支持两种调用方式:
|
||||
* 1. 新用户:通过手机号授权码完成注册并加入(phoneCode 必传)
|
||||
* 2. 已登录用户:直接加入应用(使用存储的 token)
|
||||
*/
|
||||
const handleJoinApp = async (phoneCode: string, encryptedData?: string, iv?: string) => {
|
||||
const handleJoinApp = async (phoneCode?: string, encryptedData?: string, iv?: string) => {
|
||||
try {
|
||||
setAuthLoading(true);
|
||||
|
||||
console.log('开始接受邀请, token:', token);
|
||||
// 获取 pending 的邀请信息(如果是从登录页返回)
|
||||
const pendingToken = Taro.getStorageSync('pending_invite_token') || token;
|
||||
const pendingPhoneCode = phoneCode || Taro.getStorageSync('pending_invite_phone_code');
|
||||
const pendingEncryptedData = encryptedData || Taro.getStorageSync('pending_invite_encrypted_data');
|
||||
const pendingIv = iv || Taro.getStorageSync('pending_invite_iv');
|
||||
|
||||
console.log('开始接受邀请, token:', pendingToken);
|
||||
console.log('请求URL:', `${INVITE_API_URL}/api/_app/developer/invite/accept`);
|
||||
console.log('请求参数:', { token, code: phoneCode ? '***' : null });
|
||||
console.log('请求参数:', { token: pendingToken, code: pendingPhoneCode ? '***' : null });
|
||||
|
||||
const res = await Taro.request({
|
||||
url: `${INVITE_API_URL}/api/_app/developer/invite/accept`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
token,
|
||||
code: phoneCode,
|
||||
encryptedData,
|
||||
iv
|
||||
token: pendingToken,
|
||||
code: pendingPhoneCode,
|
||||
encryptedData: pendingEncryptedData,
|
||||
iv: pendingIv
|
||||
},
|
||||
header: {
|
||||
'content-type': 'application/json',
|
||||
@@ -164,6 +243,10 @@ const InvitePage: React.FC = () => {
|
||||
console.log('响应数据:', res.data);
|
||||
|
||||
if (res.data.code === 200 || res.data.code === 0) {
|
||||
// 清除 pending 的邀请信息
|
||||
clearPendingInviteData();
|
||||
clearInviteParams();
|
||||
|
||||
Taro.showToast({ title: '加入成功', icon: 'success', duration: 1500 });
|
||||
setTimeout(() => {
|
||||
// 跳转到应用页面或首页
|
||||
@@ -171,7 +254,33 @@ const InvitePage: React.FC = () => {
|
||||
}, 1500);
|
||||
} else {
|
||||
console.error('接受邀请失败:', res.data.message, 'code:', res.data.code);
|
||||
Taro.showToast({ title: res.data.message || '加入失败', icon: 'none' });
|
||||
const errorMsg = res.data.message || '';
|
||||
|
||||
// 用户不存在,引导去登录注册
|
||||
if (errorMsg.includes('用户不存在') || errorMsg.includes('用户创建失败') || errorMsg.includes('请先登录')) {
|
||||
// 保存当前邀请信息
|
||||
Taro.setStorageSync('pending_invite_token', pendingToken);
|
||||
if (pendingPhoneCode) Taro.setStorageSync('pending_invite_phone_code', pendingPhoneCode);
|
||||
if (pendingEncryptedData) Taro.setStorageSync('pending_invite_encrypted_data', pendingEncryptedData);
|
||||
if (pendingIv) Taro.setStorageSync('pending_invite_iv', pendingIv);
|
||||
|
||||
Taro.showModal({
|
||||
title: '需要登录',
|
||||
content: '您尚未注册,请先完成登录或注册后再加入应用',
|
||||
confirmText: '去登录',
|
||||
cancelText: '取消',
|
||||
success: (modalRes) => {
|
||||
if (modalRes.confirm) {
|
||||
// 跳转到手机号授权登录页面,携带token参数以便登录后返回
|
||||
Taro.navigateTo({
|
||||
url: `/passport/phone-auth/index?redirect=${encodeURIComponent('/passport/invite/index?token=' + pendingToken)}`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Taro.showToast({ title: errorMsg || '加入失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('接受邀请异常:', err);
|
||||
@@ -181,6 +290,16 @@ const InvitePage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除 pending 的邀请数据
|
||||
*/
|
||||
const clearPendingInviteData = () => {
|
||||
Taro.removeStorageSync('pending_invite_token');
|
||||
Taro.removeStorageSync('pending_invite_phone_code');
|
||||
Taro.removeStorageSync('pending_invite_encrypted_data');
|
||||
Taro.removeStorageSync('pending_invite_iv');
|
||||
};
|
||||
|
||||
/**
|
||||
* 拒绝邀请
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user