yingsa/miniprogram/app.js
ethanfly 4719e931b0 feat(match): Enhance challenge score confirmation and match detail refresh logic
- Implemented logic to notify users when challenge scores are confirmed, prompting a refresh of match details.
- Updated the match controller to use sendToUser for sending challenge response notifications, improving notification handling.
- Added checks to ensure match details are only refreshed for the relevant match, enhancing data accuracy and user experience.
2026-02-10 01:34:09 +08:00

640 lines
22 KiB
JavaScript
Raw Permalink 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
// 已有手机号用户会直接返回 token完成登录无手机号用户需后续授权手机号
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: async (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,
};
if (data.token && data.userInfo && data.hasPhone) {
// 已有手机号的老用户:直接登录,无需授权手机号
this.globalData.token = data.token;
this.globalData.userInfo = data.userInfo;
wx.setStorageSync("token", data.token);
this.connectWebSocket();
try {
await this.ensureCurrentStore();
await this.getUserInfo(this.globalData.currentStore?.storeId);
} catch (e) {
console.error("初始化门店/用户信息失败:", e);
}
} else if (data.userInfo && data.hasPhone) {
// 兼容:若后端返回 userInfo 但未带 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);
});
},
/**
* 获取默认门店(无需登录,供新用户浏览)
*/
getDefaultStore() {
return new Promise((resolve) => {
const lastStore = wx.getStorageSync("last_store");
const params = {};
if (lastStore?.storeId) params.last_store_id = lastStore.storeId;
wx.getLocation({
type: "gcj02",
success: (loc) => {
params.latitude = loc.latitude;
params.longitude = loc.longitude;
this.request("/api/store/default", params)
.then((res) => {
if (res.data) {
this.globalData.currentStore = {
storeId: res.data.storeId,
storeName: res.data.storeName || "",
storeAddress: res.data.storeAddress || "",
};
}
resolve(this.globalData.currentStore);
})
.catch(() => resolve(null));
},
fail: () => {
this.request("/api/store/default", params)
.then((res) => {
if (res.data) {
this.globalData.currentStore = {
storeId: res.data.storeId,
storeName: res.data.storeName || "",
storeAddress: res.data.storeAddress || "",
};
}
resolve(this.globalData.currentStore);
})
.catch(() => resolve(null));
},
});
});
},
/**
* 优先使用上次选择的门店,没有或失败时再请求最近门店(新用户逻辑)
*/
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"
) {
// 检查是否是同一个比赛
const matchId = data.data?.matchId;
if (!matchId || currentPage2.data?.matchId == matchId) {
if (typeof currentPage2.loadMatchDetail === "function") {
currentPage2.loadMatchDetail();
}
}
}
break;
case "challenge_rejected":
// 挑战被拒绝
wx.showToast({ title: "对方已拒绝挑战", icon: "none" });
break;
case "challenge_score_confirmed":
// 挑战赛比分已确认
wx.showToast({ title: "比分已确认", icon: "success" });
// 如果当前在挑战赛详情页面,刷新数据
const pages3 = getCurrentPages();
const currentPage3 = pages3[pages3.length - 1];
if (
currentPage3 &&
currentPage3.route === "pages/match/challenge-detail/index"
) {
// 检查是否是同一个比赛
const matchId = data.data?.matchId;
if (!matchId || currentPage3.data?.matchId == matchId) {
if (typeof currentPage3.loadMatchDetail === "function") {
currentPage3.loadMatchDetail();
}
}
}
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 });
},
});
});
},
});