114
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 202501福大-软件工程实践 |
|---|---|
| 这个作业要求在哪里 | 软件工程实践总结&个人技术博客 |
| 这个作业的目标 | 课程回顾与总结与个人技术总结 |
在第一次作业中,我制定的学习路线主要集中在:
当前状态:整体完成了学习计划的80%,在项目实践中加深了理解,但在性能优化和架构设计方面还需要进一步提升。
在团队项目中,我担任前端技术负责人,主要解决了以下技术问题:
微信小程序登录流程优化
图表性能优化
数据同步策略
技术名称:微信小程序登录授权与用户信息管理方案
应用场景:需要获取用户身份的小程序应用,如社交、电商、内容类小程序
学习原因:微信生态中用户体系的基石,直接影响用户体验和留存率
技术难点:多端状态同步、权限管理、用户信息更新机制、性能优化
graph TD
A[用户打开小程序] --> B{检查本地token}
B -->|有效| C[静默登录成功]
B -->|无效/过期| D[发起wx.login]
D --> E[获取code]
E --> F[请求服务端]
F --> G{服务端验证}
G -->|成功| H[返回token+用户基础信息]
G -->|失败| I[重新登录流程]
H --> J[存储到Storage]
J --> K[业务页面]
L[需要用户信息] --> M{是否授权过}
M -->|是| N[直接获取]
M -->|否| O[弹出授权窗口]
O --> P[用户授权]
P --> Q[更新用户信息]
Q --> R[完成业务]
// 登录服务模块
class LoginService {
constructor() {
this.tokenKey = 'user_token';
this.userInfoKey = 'user_info';
this.tokenExpireKey = 'token_expire';
}
/**
* 检查登录状态
*/
async checkLoginStatus() {
try {
const token = wx.getStorageSync(this.tokenKey);
const expireTime = wx.getStorageSync(this.tokenExpireKey);
// 检查token是否存在且未过期
if (token && expireTime && Date.now() < expireTime) {
// 验证token有效性
const isValid = await this.validateToken(token);
if (isValid) {
return { isLogin: true, token };
}
}
return { isLogin: false };
} catch (error) {
console.error('检查登录状态失败:', error);
return { isLogin: false };
}
}
/**
* 执行登录流程
*/
async login() {
return new Promise((resolve, reject) => {
// 第一步:获取code
wx.login({
success: async (loginRes) => {
if (loginRes.code) {
try {
// 第二步:请求服务端获取token
const result = await this.requestLogin(loginRes.code);
// 第三步:存储登录信息
this.saveLoginInfo(result);
resolve({
success: true,
token: result.token,
userInfo: result.userInfo
});
} catch (error) {
reject(new Error('登录请求失败: ' + error.message));
}
} else {
reject(new Error('获取登录code失败'));
}
},
fail: (err) => {
reject(new Error('微信登录失败: ' + err.errMsg));
}
});
});
}
/**
* 请求服务端登录
*/
async requestLogin(code) {
return new Promise((resolve, reject) => {
wx.request({
url: 'https://api.example.com/auth/login',
method: 'POST',
data: {
code: code,
appid: 'your_appid', // 从配置读取
timestamp: Date.now()
},
success: (res) => {
if (res.statusCode === 200 && res.data.code === 0) {
resolve(res.data.data);
} else {
reject(new Error(res.data.message || '登录失败'));
}
},
fail: (err) => {
reject(new Error('网络请求失败: ' + err.errMsg));
}
});
});
}
/**
* 获取用户信息(按需授权)
*/
async getUserInfo(requireUserInfo = false) {
return new Promise((resolve, reject) => {
// 检查本地是否有缓存的用户信息
const cachedInfo = wx.getStorageSync(this.userInfoKey);
if (cachedInfo && !requireUserInfo) {
resolve(cachedInfo);
return;
}
// 检查授权状态
wx.getSetting({
success: (settingRes) => {
if (settingRes.authSetting['scope.userInfo']) {
// 已授权,直接获取
wx.getUserProfile({
desc: '用于完善会员资料',
success: (infoRes) => {
const userInfo = infoRes.userInfo;
this.saveUserInfo(userInfo);
resolve(userInfo);
},
fail: (err) => {
reject(new Error('获取用户信息失败: ' + err.errMsg));
}
});
} else {
// 未授权,根据参数决定是否弹窗
if (requireUserInfo) {
this.showAuthModal()
.then(resolve)
.catch(reject);
} else {
resolve(null); // 不强制获取
}
}
}
});
});
}
/**
* 显示授权弹窗
*/
showAuthModal() {
return new Promise((resolve, reject) => {
wx.showModal({
title: '用户信息授权',
content: '需要获取您的用户信息以提供个性化服务',
confirmText: '去授权',
success: (modalRes) => {
if (modalRes.confirm) {
// 跳转到授权页面或重新获取
wx.openSetting({
success: (settingRes) => {
if (settingRes.authSetting['scope.userInfo']) {
this.getUserInfo(true).then(resolve).catch(reject);
} else {
reject(new Error('用户拒绝授权'));
}
}
});
} else {
reject(new Error('用户取消授权'));
}
}
});
});
}
/**
* 存储登录信息
*/
saveLoginInfo(data) {
const { token, expires_in = 7200, userInfo } = data;
// 计算过期时间(提前5分钟过期)
const expireTime = Date.now() + (expires_in - 300) * 1000;
wx.setStorageSync(this.tokenKey, token);
wx.setStorageSync(this.tokenExpireKey, expireTime);
if (userInfo) {
this.saveUserInfo(userInfo);
}
}
/**
* 存储用户信息
*/
saveUserInfo(userInfo) {
wx.setStorageSync(this.userInfoKey, {
...userInfo,
updateTime: Date.now()
});
}
/**
* 验证token有效性
*/
async validateToken(token) {
try {
// 简化的token验证,实际应请求服务端
const expireTime = wx.getStorageSync(this.tokenExpireKey);
return expireTime && Date.now() < expireTime;
} catch (error) {
return false;
}
}
}
// 导出单例
export default new LoginService();
import loginService from '../../services/loginService';
Page({
data: {
isLogin: false,
userInfo: null,
showAuthModal: false
},
onLoad() {
this.initLogin();
},
/**
* 初始化登录状态
*/
async initLogin() {
// 显示加载中
wx.showLoading({ title: '加载中' });
try {
// 检查登录状态
const { isLogin, token } = await loginService.checkLoginStatus();
if (isLogin) {
// 已登录,获取用户信息
const userInfo = await loginService.getUserInfo(false);
this.setData({
isLogin: true,
userInfo
});
} else {
// 未登录,执行静默登录
await this.silentLogin();
}
} catch (error) {
console.error('初始化登录失败:', error);
// 静默登录失败,展示登录按钮
this.setData({ isLogin: false });
} finally {
wx.hideLoading();
}
},
/**
* 静默登录(不弹授权)
*/
async silentLogin() {
try {
await loginService.login();
const userInfo = await loginService.getUserInfo(false);
this.setData({
isLogin: true,
userInfo
});
// 登录成功事件
this.onLoginSuccess();
} catch (error) {
// 静默登录失败,记录日志但不影响用户
console.warn('静默登录失败:', error.message);
}
},
/**
* 手动登录按钮
*/
onLoginTap() {
this.setData({ showAuthModal: true });
},
/**
* 确认授权
*/
async onConfirmAuth() {
wx.showLoading({ title: '授权中' });
try {
// 先确保登录状态
if (!this.data.isLogin) {
await loginService.login();
}
// 获取完整用户信息
const userInfo = await loginService.getUserInfo(true);
this.setData({
isLogin: true,
userInfo,
showAuthModal: false
});
this.onLoginSuccess();
} catch (error) {
wx.showToast({
title: '授权失败: ' + error.message,
icon: 'none'
});
} finally {
wx.hideLoading();
}
},
/**
* 登录成功回调
*/
onLoginSuccess() {
// 更新全局状态
getApp().globalData.isLogin = true;
getApp().globalData.userInfo = this.data.userInfo;
// 触发页面更新
this.getTabBar().setData({
isLogin: true
});
// 发送登录成功事件
this.triggerEvent('loginSuccess', this.data.userInfo);
}
});
// 登录配置
export const LOGIN_CONFIG = {
// 登录相关
LOGIN_URL: 'https://api.example.com/auth/login',
TOKEN_REFRESH_URL: 'https://api.example.com/auth/refresh',
// 存储键名
STORAGE_KEYS: {
TOKEN: 'user_token',
USER_INFO: 'user_info',
TOKEN_EXPIRE: 'token_expire',
LOGIN_TIME: 'login_time'
},
// token设置
TOKEN_SETTINGS: {
EXPIRE_BUFFER: 300, // 提前5分钟过期
AUTO_REFRESH: true, // 自动刷新token
REFRESH_THRESHOLD: 0.3 // 剩余30%有效期时刷新
},
// 重试机制
RETRY_POLICY: {
MAX_RETRIES: 3,
RETRY_DELAY: 1000,
RETRY_CONDITIONS: ['networkError', 'timeout']
}
};
// 权限配置
export const AUTH_SCOPES = {
USER_INFO: 'scope.userInfo',
USER_LOCATION: 'scope.userLocation',
ADDRESS: 'scope.address',
INVOICE: 'scope.invoice'
};
// 根据环境切换配置
const ENV = process.env.NODE_ENV || 'development';
export const getLoginConfig = () => {
const baseConfig = { ...LOGIN_CONFIG };
if (ENV === 'development') {
baseConfig.LOGIN_URL = 'http://localhost:3000/auth/login';
baseConfig.TOKEN_SETTINGS.EXPIRE_BUFFER = 600; // 测试环境延长缓冲
}
return baseConfig;
};
问题描述:
用户反馈经常需要重新登录,特别是在以下场景:
排查过程:
解决方案:
// 改进的token管理方案
class EnhancedTokenManager {
constructor() {
this.localTimeOffset = 0; // 本地与服务器时间偏差
}
/**
* 同步服务器时间
*/
async syncServerTime() {
try {
const startTime = Date.now();
const response = await this.requestServerTime();
const endTime = Date.now();
const rtt = endTime - startTime;
const serverTime = response.timestamp + Math.floor(rtt / 2);
this.localTimeOffset = serverTime - Date.now();
console.log('时间同步完成,偏差:', this.localTimeOffset, 'ms');
} catch (error) {
console.warn('时间同步失败,使用本地时间');
}
}
/**
* 计算实际过期时间
*/
calculateRealExpireTime(expiresIn) {
const serverNow = Date.now() + this.localTimeOffset;
return serverNow + (expiresIn - 300) * 1000; // 提前5分钟
}
/**
* 智能token刷新
*/
async refreshTokenIfNeeded() {
const expireTime = wx.getStorageSync('token_expire');
const now = Date.now() + this.localTimeOffset;
const timeLeft = expireTime - now;
const totalTime = expireTime - wx.getStorageSync('login_time');
// 剩余时间不足30%时刷新
if (timeLeft / totalTime < 0.3) {
await this.refreshToken();
}
}
}
优化效果:
问题描述:
同一账号在不同设备登录时,旧设备token不会立即失效,存在安全风险。
解决方案:
// 设备指纹生成
function generateDeviceFingerprint() {
const systemInfo = wx.getSystemInfoSync();
return {
brand: systemInfo.brand,
model: systemInfo.model,
system: systemInfo.system,
platform: systemInfo.platform,
version: systemInfo.version,
SDKVersion: systemInfo.SDKVersion
};
}
// 服务端token管理增强
// 1. token绑定设备指纹
// 2. 新登录使旧token失效
// 3. 提供设备管理界面
问题描述:
用户首次使用时频繁弹出授权窗口,导致用户流失。
优化方案:
// 分级授权策略
class GradedAuthStrategy {
constructor() {
this.authHistory = this.loadAuthHistory();
}
/**
* 智能决定是否请求授权
*/
shouldRequestAuth(feature) {
const history = this.authHistory[feature];
if (!history) {
return true; // 首次使用
}
const { lastRequestTime, rejectCount } = history;
const daysSinceLast = (Date.now() - lastRequestTime) / (1000 * 60 * 60 * 24);
// 根据历史行为决定
if (rejectCount >= 3 && daysSinceLast < 7) {
return false; // 近期多次拒绝,暂不询问
}
if (daysSinceLast < 30 && rejectCount === 0) {
return false; // 近期已授权
}
return true;
}
/**
* 引导式授权
*/
async guidedAuth(feature, description) {
// 先展示功能价值
await this.showFeatureBenefit(feature);
// 用户确认后再请求授权
const userConfirmed = await this.confirmFeatureUse(feature);
if (userConfirmed) {
return await this.requestAuth(feature, description);
}
return null;
}
}
优化效果:
写在最后:登录授权看似简单,实则是影响用户体验的关键路径。通过本次实践,我深刻体会到"魔鬼在细节中"——每一个错误提示、每一次加载等待、每一处用户交互,都需要精心设计。好的技术方案不仅要解决功能问题,更要考虑人的使用感受。