yingsa/miniprogram/app.js
ethanfly 98f9e64d91 refactor: Enhance store and user management features
- Updated user information retrieval to support store-specific points and ladder user data.
- Refactored store initialization logic to prioritize last selected store.
- Improved API endpoints for fetching matches and user data, ensuring compatibility with store context.
- Adjusted UI components to display store-related information consistently across various pages.
- Enhanced error handling and data fetching logic for better user experience.
2026-02-07 11:09:37 +08:00

561 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const config = require("./config");
App({
globalData: {
userInfo: null,
token: null,
currentStore: null,
ladderUser: null,
wsConnected: false,
// 微信登录临时信息
wxLoginInfo: null,
// 从配置文件读取
baseUrl: config.baseUrl,
wsUrl: config.wsUrl,
},
onLaunch() {
// 从本地存储读取 token 和上次选择的门店
const token = wx.getStorageSync("token");
if (token) {
this.globalData.token = token;
this.ensureCurrentStore()
.then(() => this.getUserInfo(this.globalData.currentStore?.storeId))
.catch(() => {});
}
},
// 微信登录第一步获取openid和session_key
wxLogin() {
return new Promise((resolve, reject) => {
wx.login({
success: (res) => {
wx.request({
url: `${this.globalData.baseUrl}/api/user/login`,
method: "POST",
data: { code: res.code },
success: (loginRes) => {
if (loginRes.data.code === 0) {
const data = loginRes.data.data;
// 保存微信登录信息(用于后续手机号授权)
this.globalData.wxLoginInfo = {
openid: data.openid,
unionid: data.unionid,
sessionKey: data.sessionKey,
isNewUser: data.isNewUser,
hasPhone: data.hasPhone,
};
// 如果已有token老用户直接使用
if (data.userInfo && data.hasPhone) {
// 老用户已绑定手机号生成token并登录
this.globalData.userInfo = data.userInfo;
}
resolve(data);
} else {
reject(loginRes.data);
}
},
fail: reject,
});
},
fail: reject,
});
});
},
// 手机号授权登录(第二步:解密手机号完成注册/登录)
phoneLogin(encryptedData, iv, gender) {
return new Promise((resolve, reject) => {
const wxInfo = this.globalData.wxLoginInfo;
if (!wxInfo) {
reject({ message: "请先进行微信登录" });
return;
}
const requestData = {
openid: wxInfo.openid,
unionid: wxInfo.unionid,
sessionKey: wxInfo.sessionKey,
encryptedData,
iv,
};
// 如果提供了性别参数,添加到请求中
if (gender === 1 || gender === 2) {
requestData.gender = gender;
}
wx.request({
url: `${this.globalData.baseUrl}/api/user/phone-login`,
method: "POST",
data: requestData,
success: (loginRes) => {
if (loginRes.data.code === 0) {
this.globalData.token = loginRes.data.data.token;
this.globalData.userInfo = loginRes.data.data.userInfo;
// 处理天梯用户信息(战力按当前门店,当前门店无天梯则不显示)
if (
loginRes.data.data.userInfo.ladderUsers &&
loginRes.data.data.userInfo.ladderUsers.length > 0
) {
if (
this.globalData.currentStore &&
this.globalData.currentStore.storeId
) {
const currentStoreLadderUser =
loginRes.data.data.userInfo.ladderUsers.find(
(lu) => lu.storeId === this.globalData.currentStore.storeId,
);
// 仅当当前门店有天梯用户时才显示,否则为 null避免显示其他门店数据
this.globalData.ladderUser = currentStoreLadderUser || null;
} else {
this.globalData.ladderUser = null;
}
} else {
this.globalData.ladderUser = null;
}
wx.setStorageSync("token", loginRes.data.data.token);
this.connectWebSocket();
resolve(loginRes.data.data);
} else {
reject(loginRes.data);
}
},
fail: reject,
});
});
},
// 旧的登录方法(兼容)
login() {
return this.wxLogin();
},
// 获取用户信息storeId 可选,传入时返回该门店的积分 storePoints
getUserInfo(storeId) {
return new Promise((resolve, reject) => {
const data = storeId ? { store_id: storeId } : {};
this.request("/api/user/info", data)
.then((res) => {
this.globalData.userInfo = res.data;
// 处理天梯用户信息(战力按当前门店,当前门店无天梯则不显示)
if (res.data.ladderUsers && res.data.ladderUsers.length > 0) {
if (
this.globalData.currentStore &&
this.globalData.currentStore.storeId
) {
const currentStoreLadderUser = res.data.ladderUsers.find(
(lu) => lu.storeId === this.globalData.currentStore.storeId,
);
// 仅当当前门店有天梯用户时才显示,否则为 null避免显示其他门店数据
this.globalData.ladderUser = currentStoreLadderUser || null;
} else {
this.globalData.ladderUser = null;
}
} else {
this.globalData.ladderUser = null;
}
this.connectWebSocket();
resolve(res.data);
})
.catch(reject);
});
},
/**
* 优先使用上次选择的门店,没有或失败时再请求最近门店(新用户逻辑)
*/
ensureCurrentStore() {
const lastStore = wx.getStorageSync("last_store");
if (lastStore && lastStore.storeId) {
this.globalData.currentStore = {
storeId: lastStore.storeId,
storeName: lastStore.storeName || "",
storeAddress: lastStore.storeAddress || "",
};
return this.getLadderUser(lastStore.storeId)
.then(() => this.globalData.currentStore)
.catch(() => {
wx.removeStorageSync("last_store");
return this.getCurrentStore();
});
}
return this.getCurrentStore();
},
// 获取当前门店(新用户或未选过门店时:按位置取最近门店)
getCurrentStore() {
return new Promise((resolve, reject) => {
wx.getLocation({
type: "gcj02",
success: (loc) => {
this.request("/api/user/current-store", {
latitude: loc.latitude,
longitude: loc.longitude,
})
.then((res) => {
this.globalData.currentStore = res.data;
if (res.data && res.data.ladderUserId) {
this.getLadderUser(res.data.storeId);
} else if (res.data && res.data.storeId) {
if (
this.globalData.userInfo &&
this.globalData.userInfo.ladderUsers
) {
const currentStoreLadderUser =
this.globalData.userInfo.ladderUsers.find(
(lu) => lu.storeId === res.data.storeId,
);
if (currentStoreLadderUser) {
this.globalData.ladderUser = currentStoreLadderUser;
} else {
this.globalData.ladderUser = null;
}
}
}
resolve(res.data);
})
.catch(reject);
},
fail: () => {
this.request("/api/user/current-store")
.then((res) => {
this.globalData.currentStore = res.data;
if (res.data && res.data.ladderUserId) {
this.getLadderUser(res.data.storeId);
} else if (res.data && res.data.storeId) {
if (
this.globalData.userInfo &&
this.globalData.userInfo.ladderUsers
) {
const currentStoreLadderUser =
this.globalData.userInfo.ladderUsers.find(
(lu) => lu.storeId === res.data.storeId,
);
if (currentStoreLadderUser) {
this.globalData.ladderUser = currentStoreLadderUser;
} else {
this.globalData.ladderUser = null;
}
}
}
resolve(res.data);
})
.catch(reject);
},
});
});
},
// 获取天梯用户信息(请求失败时 reject便于上层做降级
getLadderUser(storeId) {
return this.request("/api/user/ladder-info", { store_id: storeId }).then(
(res) => {
if (res.data && res.data.length > 0) {
this.globalData.ladderUser = res.data[0];
} else {
this.globalData.ladderUser = null;
}
return res.data;
},
);
},
// WebSocket连接
connectWebSocket() {
if (this.globalData.wsConnected || !this.globalData.token) return;
const wsUrl = this.globalData.wsUrl || "ws://localhost:3000/ws";
this.ws = wx.connectSocket({
url: wsUrl,
success: () => {
console.log("WebSocket连接中...");
},
});
wx.onSocketOpen(() => {
console.log("WebSocket已连接");
this.globalData.wsConnected = true;
// 发送认证
wx.sendSocketMessage({
data: JSON.stringify({
type: "auth",
token: this.globalData.token,
}),
});
});
wx.onSocketMessage((res) => {
const data = JSON.parse(res.data);
this.handleWsMessage(data);
});
wx.onSocketClose(() => {
console.log("WebSocket已断开");
this.globalData.wsConnected = false;
// 尝试重连
setTimeout(() => {
this.connectWebSocket();
}, 5000);
});
wx.onSocketError((err) => {
console.error("WebSocket错误:", err);
});
},
// 处理WebSocket消息
handleWsMessage(data) {
switch (data.type) {
case "challenge_request":
// 收到挑战请求 - 使用自定义弹框
const challengeData = data.data;
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
// 如果当前页面有处理挑战请求的方法,调用它
if (
currentPage &&
typeof currentPage.handleChallengeRequest === "function"
) {
currentPage.handleChallengeRequest(challengeData);
} else {
// 否则使用系统弹框
wx.showModal({
title: "收到挑战",
content: `${challengeData.challenger.realName}(Lv${challengeData.challenger.level}, 战力${challengeData.challenger.powerScore}) 向你发起挑战`,
confirmText: "接受",
cancelText: "拒绝",
success: (res) => {
this.request(
"/api/match/challenge/respond",
{
match_id: challengeData.matchId,
accept: res.confirm,
},
"POST",
)
.then(() => {
if (res.confirm) {
wx.showToast({ title: "已接受挑战", icon: "success" });
// 跳转到挑战赛详情
setTimeout(() => {
wx.navigateTo({
url: `/pages/match/challenge-detail/index?id=${challengeData.matchId}`,
});
}, 1500);
} else {
wx.showToast({ title: "已拒绝挑战", icon: "success" });
}
})
.catch((err) => {
console.error("响应挑战失败:", err);
wx.showToast({ title: "操作失败", icon: "none" });
});
},
});
}
break;
case "challenge_accepted":
// 挑战被接受
wx.showToast({ title: "对方已接受挑战", icon: "success" });
// 如果当前在挑战赛详情页面,刷新数据
const pages2 = getCurrentPages();
const currentPage2 = pages2[pages2.length - 1];
if (
currentPage2 &&
currentPage2.route === "pages/match/challenge-detail/index"
) {
if (typeof currentPage2.loadMatchDetail === "function") {
currentPage2.loadMatchDetail();
}
}
break;
case "challenge_rejected":
// 挑战被拒绝
wx.showToast({ title: "对方已拒绝挑战", icon: "none" });
break;
case "score_confirm_request":
// 收到比分确认请求
wx.showModal({
title: "确认比分",
content: `比分: ${data.data.player1Score} : ${data.data.player2Score}`,
confirmText: "确认",
cancelText: "有争议",
success: (res) => {
this.request(
"/api/match/challenge/confirm-score",
{
game_id: data.data.gameId,
confirm: res.confirm,
},
"POST",
);
},
});
break;
case "match_paired":
// 排位赛匹配通知
try {
const payload = data.data || {};
const opponent = payload.opponent || {};
const matchCode = payload.matchCode;
wx.showModal({
title: "匹配成功",
content: `你的对手是: ${opponent.realName || "对手"}`,
showCancel: false,
success: (res) => {
if (res.confirm) {
// 检查当前页面
const pagesMatch = getCurrentPages();
const currentMatchPage = pagesMatch[pagesMatch.length - 1];
if (currentMatchPage && currentMatchPage.route === "pages/match/ranking/index") {
// 如果已在比赛详情页,检查是否是同一场比赛
const targetCode = matchCode;
if (
!targetCode ||
!currentMatchPage.data ||
currentMatchPage.data.matchCode === targetCode
) {
// 刷新详情页的所有接口信息
if (typeof currentMatchPage.fetchMatchDetail === "function") {
currentMatchPage.fetchMatchDetail();
} else if (typeof currentMatchPage.fetchCurrentGame === "function") {
currentMatchPage.fetchCurrentGame();
}
} else {
// 不同比赛,跳转到新的比赛详情页
wx.redirectTo({
url: `/pages/match/ranking/index?code=${targetCode}`
});
}
} else {
// 如果不在比赛详情页,自动跳转到比赛详情页
if (matchCode) {
wx.navigateTo({
url: `/pages/match/ranking/index?code=${matchCode}`
});
}
}
}
}
});
} catch (e) {
console.error("处理 match_paired 消息失败:", e);
}
break;
case "ranking_game_updated":
// 排位赛比分/匹配更新:刷新当前排位赛详情页
try {
const pages3 = getCurrentPages();
const currentPage3 = pages3[pages3.length - 1];
if (currentPage3) {
if (currentPage3.route === "pages/match/ranking/index") {
const targetCode = data.data && data.data.matchCode;
if (
!targetCode ||
!currentPage3.data ||
currentPage3.data.matchCode === targetCode
) {
if (typeof currentPage3.fetchMatchDetail === "function") {
currentPage3.fetchMatchDetail();
}
}
} else if (currentPage3.route === "pages/match/challenge/index") {
// 比赛首页:比分确认后也刷新“当前比赛”卡片
if (typeof currentPage3.fetchOngoingMatches === "function") {
currentPage3.fetchOngoingMatches();
}
}
}
} catch (e) {
console.error("处理 ranking_game_updated 消息失败:", e);
}
break;
case "points_order_verified":
// 后台核销积分订单后,通知小程序刷新订单列表/详情
try {
wx.showToast({ title: "积分订单已核销", icon: "success" });
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const payload = data.data || {};
if (
currentPage &&
currentPage.route === "pages/points/order/index"
) {
// 如果页面实现了下拉刷新逻辑,优先复用
if (typeof currentPage.onPullDownRefresh === "function") {
currentPage.onPullDownRefresh();
} else if (typeof currentPage.fetchOrders === "function") {
// 兜底:重置分页并主动刷新
currentPage.setData({ page: 1, hasMore: true });
currentPage.fetchOrders();
}
// 精确刷新当前弹窗和列表中的该笔订单
if (
payload.orderId &&
typeof currentPage.refreshOrderById === "function"
) {
currentPage.refreshOrderById(payload.orderId);
}
}
} catch (e) {
console.error("处理 points_order_verified 消息失败:", e);
}
break;
}
},
// 封装请求
request(url, data = {}, method = "GET") {
return new Promise((resolve, reject) => {
const showErrorToast = (message) => {
const title =
(message && String(message).trim()) || "网络异常,请稍后重试";
wx.showToast({ title, icon: "none" });
};
wx.request({
url: `${this.globalData.baseUrl}${url}`,
method,
data,
header: {
Authorization: `Bearer ${this.globalData.token}`,
},
success: (res) => {
if (res.data.code === 0) {
resolve(res.data);
} else if (res.data.code === 401) {
// 登录过期
this.globalData.token = null;
wx.removeStorageSync("token");
showErrorToast("登录已过期,请重新登录");
wx.reLaunch({ url: "/pages/user/index" });
reject({ message: res.data.message || "登录已过期", ...res.data });
} else {
showErrorToast(res.data.message || "请求失败");
reject({ message: res.data.message || "请求失败", ...res.data });
}
},
fail: (err) => {
showErrorToast("网络异常,请检查网络后重试");
reject({ message: "网络异常,请检查网络后重试", err });
},
});
});
},
});