feat: 优化

This commit is contained in:
2026-01-18 02:55:02 +08:00
parent 85073b7960
commit a0378b5cbd
3 changed files with 165 additions and 200 deletions

View File

@@ -12,26 +12,29 @@ const SERVER_BASE = API_BASE.APP_MEMBER
* @param {Object} info - 包含 accessToken 和 refreshToken 的对象
*/
function saveTokens(info) {
if (info?.accessToken || info?.refreshToken) {
tokenManager.setTokens({
accessToken: info.accessToken || '',
refreshToken: info.refreshToken || '',
expiresTime: info.expiresTime || 0, // 直接传递,由 token-manager 处理格式转换
tokenType: info.tokenType || 'Bearer'
})
if (!info?.accessToken && !info?.refreshToken) {
return
}
}
tokenManager.setTokens({
accessToken: info.accessToken || '',
refreshToken: info.refreshToken || '',
expiresTime: info.expiresTime || 0,
tokenType: info.tokenType || 'Bearer'
})
}
/**
* 响应拦截(可选):统一错误处理
* 清除用户信息缓存
*/
// api.interceptors.response.use(
// (resp) => resp,
// (err) => {
// // 统一错误日志/提示
// return Promise.reject(err);
// }
// );
async function clearUserCache() {
try {
const { clearUserInfoCache } = await import('@gold/hooks/web/useUserInfo')
clearUserInfoCache()
} catch (e) {
// 清除缓存失败不影响登录流程
}
}
/**
* 短信场景枚举(请与后端配置保持一致)
@@ -50,8 +53,8 @@ export const SMS_SCENE = {
* 短信模板编码常量
*/
export const SMS_TEMPLATE_CODE = {
USER_REGISTER: 'muye-user-code', // 用户注册模板编码
};
USER_REGISTER: 'muye-user-code',
}
/**
* 账号密码登录
@@ -62,19 +65,11 @@ export const SMS_TEMPLATE_CODE = {
* @returns {Promise<Object>} data.data: { accessToken, refreshToken, expiresTime, userInfo }
*/
export async function loginByPassword(mobile, password) {
const { data } = await api.post(`${SERVER_BASE}/auth/login`, { mobile, password });
const info = data || {};
saveTokens(info);
// 清除用户信息缓存,确保登录后获取最新信息
try {
const { clearUserInfoCache } = await import('@gold/hooks/web/useUserInfo')
clearUserInfoCache()
} catch (e) {
// 清除缓存失败不影响登录流程
}
return info;
const { data } = await api.post(`${SERVER_BASE}/auth/login`, { mobile, password })
const info = data || {}
saveTokens(info)
await clearUserCache()
return info
}
/**
@@ -87,11 +82,12 @@ export async function loginByPassword(mobile, password) {
* @returns {Promise<Object>} 后端通用响应结构
*/
export async function sendSmsCode(mobile, scene, templateCode) {
const body = { scene };
if (mobile) body.mobile = mobile; // 修改密码场景后端会根据当前登录用户自动填充手机号
if (templateCode) body.templateCode = templateCode; // 添加模板编码参数
const { data } = await api.post(`${SERVER_BASE}/auth/send-sms-code`, body);
return data;
const body = { scene }
if (mobile) body.mobile = mobile
if (templateCode) body.templateCode = templateCode
const { data } = await api.post(`${SERVER_BASE}/auth/send-sms-code`, body)
return data
}
/**
@@ -104,8 +100,8 @@ export async function sendSmsCode(mobile, scene, templateCode) {
* @returns {Promise<Object>} 后端通用响应结构
*/
export async function validateSmsCode(mobile, code, scene) {
const { data } = await api.post(`${SERVER_BASE}/auth/validate-sms-code`, { mobile, code, scene });
return data;
const { data } = await api.post(`${SERVER_BASE}/auth/validate-sms-code`, { mobile, code, scene })
return data
}
/**
@@ -117,19 +113,11 @@ export async function validateSmsCode(mobile, code, scene) {
* @returns {Promise<Object>} data.data: { accessToken, refreshToken, expiresTime, userInfo }
*/
export async function loginBySms(mobile, code) {
const { data } = await api.post(`${SERVER_BASE}/auth/sms-login`, { mobile, code });
const info = data || {};
saveTokens(info);
// 清除用户信息缓存,确保登录后获取最新信息
try {
const { clearUserInfoCache } = await import('@gold/hooks/web/useUserInfo')
clearUserInfoCache()
} catch (e) {
// 清除缓存失败不影响登录流程
}
return info;
const { data } = await api.post(`${SERVER_BASE}/auth/sms-login`, { mobile, code })
const info = data || {}
saveTokens(info)
await clearUserCache()
return info
}
/**
@@ -139,26 +127,36 @@ export async function loginBySms(mobile, code) {
* @returns {Promise<Object>} data.data: { accessToken, refreshToken, expiresTime, userInfo }
*/
export async function refreshToken() {
const rt = tokenManager.getRefreshToken();
if (!rt) throw new Error('缺少 refresh_token');
// refreshToken 作为 URL 查询参数传递
const { data } = await api.post(`${SERVER_BASE}/auth/refresh-token`, null, { params: { refreshToken: rt } });
const info = data || {};
saveTokens(info);
const rt = tokenManager.getRefreshToken()
if (!rt) throw new Error('缺少 refresh_token')
// 清除用户信息缓存,因为 token 刷新后用户信息可能已更新
try {
const { clearUserInfoCache } = await import('@gold/hooks/web/useUserInfo')
clearUserInfoCache()
} catch (e) {
// 清除缓存失败不影响登录流程
const { data } = await api.post(`${SERVER_BASE}/auth/refresh-token`, null, { params: { refreshToken: rt } })
// 检查业务状态码
if (data?.code === 401) {
// 401: refreshToken 无效或过期
tokenManager.clearTokens()
await clearUserCache()
router.push('/login')
const authError = new Error('登录已过期,请重新登录')
authError.code = 401
authError.needRelogin = true
throw authError
}
return info;
if (data?.code !== 0 && data?.code !== 200) {
throw new Error(data?.message || data?.msg || '刷新token失败')
}
const info = data || {}
saveTokens(info)
await clearUserCache()
return info
}
/**
* 登录态下:发送修改密码验证码
* 登录态下:发送"修改密码"验证码
* - 场景SMS_SCENE.MEMBER_UPDATE_PASSWORD
* - 特性:后端会以当前登录用户的手机号为准,无需传 mobile
* POST /member/auth/send-sms-code
@@ -168,8 +166,8 @@ export async function refreshToken() {
export async function sendUpdatePasswordCode() {
const { data } = await api.post(`${SERVER_BASE}/auth/send-sms-code`, {
scene: SMS_SCENE.MEMBER_UPDATE_PASSWORD,
});
return data;
})
return data
}
/**
@@ -184,12 +182,12 @@ export async function updatePasswordBySmsCode(newPassword, smsCode) {
const { data } = await api.put(`${SERVER_BASE}/user/update-password`, {
password: newPassword,
code: smsCode,
});
return data;
})
return data
}
/**
* 未登录:发送忘记密码验证码
* 未登录:发送"忘记密码"验证码
* - 场景SMS_SCENE.MEMBER_RESET_PASSWORD
* POST /member/auth/send-sms-code
*
@@ -200,8 +198,8 @@ export async function sendResetPasswordCode(mobile) {
const { data } = await api.post(`${SERVER_BASE}/auth/send-sms-code`, {
mobile,
scene: SMS_SCENE.MEMBER_RESET_PASSWORD,
});
return data;
})
return data
}
/**
@@ -218,8 +216,8 @@ export async function resetPasswordBySms(mobile, newPassword, smsCode) {
mobile,
password: newPassword,
code: smsCode,
});
return data;
})
return data
}
/**
@@ -234,11 +232,11 @@ export async function getUserInfo() {
}
/**
* 手机+验证码+密码注册组合流程(基于短信登录即注册 + 设置密码)
* "手机+验证码+密码注册"组合流程(基于短信登录即注册 + 设置密码)
* 说明:
* - 1) 发送登录场景验证码(可选:若已拿到 code 可跳过)
* - 2) 短信登录:首次会自动注册并返回 token
* - 3) 登录态下发送修改密码验证码
* - 3) 登录态下发送"修改密码"验证码
* - 4) 用短信验证码设置密码
*
* @param {string} mobile - 手机号(必填)
@@ -248,17 +246,11 @@ export async function getUserInfo() {
* @returns {Promise<void>}
*/
export async function registerWithMobileCodePassword(mobile, loginCode, newPassword, updatePwdCode) {
// 1) 可选:发送登录场景验证码(若已拿到 loginCode 可跳过)
// await sendSmsCode(mobile, SMS_SCENE.MEMBER_LOGIN);
// 2) 短信登录(首次即注册)
await loginBySms(mobile, loginCode);
// 3) 登录态下发送“修改密码”验证码(短信会发到当前账号绑定的手机号)
// await sendUpdatePasswordCode();
await loginBySms(mobile, loginCode)
// 4) 用短信验证码设置密码
await updatePasswordBySmsCode(newPassword, updatePwdCode);
await updatePasswordBySmsCode(newPassword, updatePwdCode)
}
/**

View File

@@ -22,29 +22,16 @@ export function createHttpClient(options = {}) {
const httpClient = createClientAxios({
baseURL: '/',
timeout: 180000,
refreshTokenFn: refreshToken, // 传递刷新函数给拦截器
refreshTokenFn: refreshToken,
on401: async (error) => {
// 401优先使用上层自定义处理
if (on401) {
await on401(error)
return
}
// 默认处理尝试刷新token
try {
await refreshToken()
error._handled = true
error._tokenRefreshed = true
} catch (refreshError) {
router.push('/login')
}
router.push('/login')
},
on403: (error) => {
if (on403) {
on403(error)
} else {
router.push('/login')
}
on403 ? on403(error) : router.push('/login')
},
})
@@ -74,38 +61,16 @@ export default http
*
* const myHttp = createHttpClient()
* myHttp.interceptors.request.use((config) => {
* // 添加自定义请求头
* config.headers['X-Custom-Header'] = 'value'
* return config
* })
* await myHttp.post('/api/data')
*
* 3. 自定义 401 处理
* import { createHttpClient } from '@/api/http'
*
* const myHttp = createHttpClient({
* on401: (error) => {
* // 自定义 401 处理逻辑
* console.log('自定义 401 处理')
* }
* })
*
* 4. 高级用法:自动刷新 token 并重试请求
* import { createHttpClient } from '@/api/http'
*
* const myHttp = createHttpClient()
*
* try {
* const response = await myHttp.post('/api/data')
* console.log(response.data)
* } catch (error) {
* // 如果是 401 且刷新失败,会自动调用 onAuthFailed 回调
* console.error('请求失败:', error.message)
* }
*
* 注意:当 401 发生时,系统会自动尝试刷新 token。
* 如果刷新成功,会使用相同的 http 实例重新发起请求,
* 确保自定义拦截器被正确应用。
*/