refactor: Improve data validation and avatar normalization

- Simplified data validation logic in the index.js file to handle cases where data is missing or incomplete.
- Updated the index.wxml file to provide a clearer message when no ranking data is available.
- Added a new method to normalize avatar URLs in the helper.js file, ensuring that default avatars are handled correctly across user and match controllers.
- Refactored user and match controllers to utilize the new avatar normalization method, enhancing consistency in avatar display.
This commit is contained in:
ethanfly 2026-02-10 01:27:52 +08:00
parent 21c5bb9b57
commit e871eed0e8
7 changed files with 89 additions and 75 deletions

View File

@ -156,17 +156,7 @@ Page({
}); });
const data = res && res.data ? res.data : null; const data = res && res.data ? res.data : null;
if (!data) return; if (!data || !data.ladderUserId || !data.page) {
if (data.qualified === false) {
wx.showToast({
title: `本月场次不足(${data.monthlyMatchCount}/${data.minMonthlyMatches}`,
icon: "none",
});
return;
}
if (!data.ladderUserId || !data.page) {
return; return;
} }

View File

@ -71,7 +71,7 @@
<view wx:elif="{{!loading}}" class="empty-state"> <view wx:elif="{{!loading}}" class="empty-state">
<image class="empty-icon" src="/images/empty-ranking.svg" mode="aspectFit"></image> <image class="empty-icon" src="/images/empty-ranking.svg" mode="aspectFit"></image>
<text class="empty-title">暂无排名数据</text> <text class="empty-title">暂无排名数据</text>
<text class="empty-desc">每月完成3场比赛即可上榜</text> <text class="empty-desc">暂无排名数据</text>
</view> </view>
<!-- 加载更多 --> <!-- 加载更多 -->
<view wx:if="{{loading}}" class="loading-state"> <view wx:if="{{loading}}" class="loading-state">

View File

@ -31,6 +31,19 @@ Page({
} }
}, },
onPullDownRefresh() {
// 下拉刷新时重新加载比赛详情
if (this.data.matchId) {
this.loadMatchDetail().then(() => {
wx.stopPullDownRefresh()
}).catch(() => {
wx.stopPullDownRefresh()
})
} else {
wx.stopPullDownRefresh()
}
},
// 加载比赛详情 // 加载比赛详情
async loadMatchDetail() { async loadMatchDetail() {
this.setData({ loading: true }) this.setData({ loading: true })

View File

@ -37,13 +37,6 @@ class LadderController {
status: 1, status: 1,
}; };
// 如果不是大屏显示,则需要满足每月最低参赛场次限制
if (!is_display) {
where.monthly_match_count = {
[Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES,
};
}
if (gender) { if (gender) {
where.gender = gender; where.gender = gender;
} }
@ -87,13 +80,19 @@ class LadderController {
// 添加排名 // 添加排名
const startRank = offset + 1; const startRank = offset + 1;
const list = rows.map((lu, index) => ({ const list = rows.map((lu, index) => {
// 如果头像是服务端的默认头像URL返回null让前端使用本地默认头像
let avatar = lu.user?.avatar;
if (avatar && (avatar.includes('/images/avatar-default.svg') || avatar.includes('avatar-default'))) {
avatar = null;
}
return {
rank: startRank + index, rank: startRank + index,
id: lu.id, id: lu.id,
userId: lu.user_id, userId: lu.user_id,
realName: lu.real_name, realName: lu.real_name,
nickname: lu.user?.nickname, nickname: lu.user?.nickname,
avatar: lu.user?.avatar, avatar: avatar || null,
gender: lu.gender, gender: lu.gender,
level: lu.level, level: lu.level,
levelName: LADDER_LEVEL_NAMES[lu.level], levelName: LADDER_LEVEL_NAMES[lu.level],
@ -104,7 +103,8 @@ class LadderController {
lu.match_count > 0 lu.match_count > 0
? Math.round((lu.win_count / lu.match_count) * 100) ? Math.round((lu.win_count / lu.match_count) * 100)
: 0, : 0,
})); };
});
res.json(pageResult(list, count, page, pageSize)); res.json(pageResult(list, count, page, pageSize));
} catch (err) { } catch (err) {
@ -149,31 +149,11 @@ class LadderController {
return res.status(404).json(error("未加入该门店天梯", 404)); return res.status(404).json(error("未加入该门店天梯", 404));
} }
if (
!is_display &&
(ladderUser.monthly_match_count || 0) < POWER_CALC.MIN_MONTHLY_MATCHES
) {
return res.json(
success({
qualified: false,
ladderUserId: ladderUser.id,
monthlyMatchCount: ladderUser.monthly_match_count || 0,
minMonthlyMatches: POWER_CALC.MIN_MONTHLY_MATCHES,
}),
);
}
const where = { const where = {
store_id, store_id,
status: 1, status: 1,
}; };
if (!is_display) {
where.monthly_match_count = {
[Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES,
};
}
if (normalizedGender) { if (normalizedGender) {
where.gender = normalizedGender; where.gender = normalizedGender;
} }
@ -233,17 +213,22 @@ class LadderController {
gender: ladderUser.gender, gender: ladderUser.gender,
status: 1, status: 1,
power_score: { [Op.gt]: ladderUser.power_score }, power_score: { [Op.gt]: ladderUser.power_score },
monthly_match_count: { [Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES },
}, },
}); });
// 如果头像是服务端的默认头像URL返回null让前端使用本地默认头像
let avatar = ladderUser.user?.avatar;
if (avatar && (avatar.includes('/images/avatar-default.svg') || avatar.includes('avatar-default'))) {
avatar = null;
}
res.json( res.json(
success({ success({
id: ladderUser.id, id: ladderUser.id,
userId: ladderUser.user_id, userId: ladderUser.user_id,
realName: ladderUser.real_name, realName: ladderUser.real_name,
nickname: ladderUser.user?.nickname, nickname: ladderUser.user?.nickname,
avatar: ladderUser.user?.avatar, avatar: avatar || null,
memberCode: ladderUser.user?.member_code, memberCode: ladderUser.user?.member_code,
gender: ladderUser.gender, gender: ladderUser.gender,
level: ladderUser.level, level: ladderUser.level,
@ -300,20 +285,25 @@ class LadderController {
gender: ladderUser.gender, gender: ladderUser.gender,
status: 1, status: 1,
power_score: { [Op.gt]: ladderUser.power_score }, power_score: { [Op.gt]: ladderUser.power_score },
monthly_match_count: { [Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES },
}, },
}); });
const matchCount = ladderUser.match_count || 0; const matchCount = ladderUser.match_count || 0;
const winCount = ladderUser.win_count || 0; const winCount = ladderUser.win_count || 0;
// 如果头像是服务端的默认头像URL返回null让前端使用本地默认头像
let avatar = ladderUser.user && ladderUser.user.avatar;
if (avatar && (avatar.includes('/images/avatar-default.svg') || avatar.includes('avatar-default'))) {
avatar = null;
}
res.json( res.json(
success({ success({
id: ladderUser.id, id: ladderUser.id,
userId: ladderUser.user_id, userId: ladderUser.user_id,
realName: ladderUser.real_name, realName: ladderUser.real_name,
nickname: ladderUser.user && ladderUser.user.nickname, nickname: ladderUser.user && ladderUser.user.nickname,
avatar: ladderUser.user && ladderUser.user.avatar, avatar: avatar || null,
memberCode: ladderUser.user && ladderUser.user.member_code, memberCode: ladderUser.user && ladderUser.user.member_code,
gender: ladderUser.gender, gender: ladderUser.gender,
level: ladderUser.level, level: ladderUser.level,

View File

@ -1,6 +1,6 @@
const { Match, MatchGame, MatchPlayer, MatchRound, LadderUser, User, Store, sequelize } = require('../models'); const { Match, MatchGame, MatchPlayer, MatchRound, LadderUser, User, Store, sequelize } = require('../models');
const { MATCH_TYPES, MATCH_STATUS, CONFIRM_STATUS, RANKING_STAGE, MATCH_WEIGHTS, POWER_CALC } = require('../config/constants'); const { MATCH_TYPES, MATCH_STATUS, CONFIRM_STATUS, RANKING_STAGE, MATCH_WEIGHTS, POWER_CALC } = require('../config/constants');
const { generateMatchCode, success, error, getPagination, pageResult } = require('../utils/helper'); const { generateMatchCode, success, error, getPagination, pageResult, normalizeAvatarForClient } = require('../utils/helper');
const PowerCalculator = require('../services/powerCalculator'); const PowerCalculator = require('../services/powerCalculator');
const { sendChallengeNotification, sendScoreConfirmNotification, sendMatchNotification, broadcastToUsers } = require('../websocket'); const { sendChallengeNotification, sendScoreConfirmNotification, sendMatchNotification, broadcastToUsers } = require('../websocket');
const matchAdminController = require('./matchAdminController'); const matchAdminController = require('./matchAdminController');
@ -949,7 +949,7 @@ class MatchController {
ladderUserId: p.ladder_user_id, ladderUserId: p.ladder_user_id,
realName: p.ladderUser?.real_name || `选手${p.ladder_user_id}`, realName: p.ladderUser?.real_name || `选手${p.ladder_user_id}`,
nickname: p.ladderUser?.user?.nickname, nickname: p.ladderUser?.user?.nickname,
avatar: p.ladderUser?.user?.avatar, avatar: normalizeAvatarForClient(p.ladderUser?.user?.avatar, req),
level: p.ladderUser?.level level: p.ladderUser?.level
}); });
} }
@ -1005,7 +1005,7 @@ class MatchController {
ladderUserId: p.ladder_user_id, ladderUserId: p.ladder_user_id,
realName: p.ladderUser?.real_name, realName: p.ladderUser?.real_name,
nickname: p.ladderUser?.user?.nickname, nickname: p.ladderUser?.user?.nickname,
avatar: p.ladderUser?.user?.avatar, avatar: normalizeAvatarForClient(p.ladderUser?.user?.avatar, req),
level: p.ladderUser?.level, level: p.ladderUser?.level,
initialPower: p.initial_power, initialPower: p.initial_power,
winCount: p.win_count, winCount: p.win_count,
@ -1140,7 +1140,7 @@ class MatchController {
id: opponent.id, id: opponent.id,
realName: opponent.real_name, realName: opponent.real_name,
nickname: opponent.user?.nickname, nickname: opponent.user?.nickname,
avatar: opponent.user?.avatar, avatar: normalizeAvatarForClient(opponent.user?.avatar, req),
level: opponent.level, level: opponent.level,
powerScore: opponent.power_score powerScore: opponent.power_score
}, },
@ -1433,7 +1433,7 @@ class MatchController {
id: opponent.id, id: opponent.id,
realName: opponent.real_name, realName: opponent.real_name,
nickname: opponent.user && opponent.user.nickname, nickname: opponent.user && opponent.user.nickname,
avatar: opponent.user && opponent.user.avatar, avatar: normalizeAvatarForClient(opponent.user && opponent.user.avatar, req),
level: opponent.level, level: opponent.level,
powerScore: opponent.power_score powerScore: opponent.power_score
} }
@ -1544,7 +1544,7 @@ class MatchController {
id: opponentLadder.id, id: opponentLadder.id,
realName: opponentLadder.real_name, realName: opponentLadder.real_name,
nickname: opponentLadder.user?.nickname, nickname: opponentLadder.user?.nickname,
avatar: opponentLadder.user?.avatar, avatar: normalizeAvatarForClient(opponentLadder.user?.avatar, req),
level: opponentLadder.level, level: opponentLadder.level,
powerScore: opponentLadder.power_score powerScore: opponentLadder.power_score
}; };
@ -1823,7 +1823,7 @@ class MatchController {
id: challengerLadder.id, id: challengerLadder.id,
realName: challengerLadder.real_name, realName: challengerLadder.real_name,
nickname: challengerLadder.user?.nickname, nickname: challengerLadder.user?.nickname,
avatar: challengerLadder.user?.avatar, avatar: normalizeAvatarForClient(challengerLadder.user?.avatar, req),
level: challengerLadder.level, level: challengerLadder.level,
powerScore: challengerLadder.power_score, powerScore: challengerLadder.power_score,
userId: challengerLadder.user_id, // 添加 user_id用于前端判断 userId: challengerLadder.user_id, // 添加 user_id用于前端判断
@ -1836,7 +1836,7 @@ class MatchController {
id: defenderLadder.id, id: defenderLadder.id,
realName: defenderLadder.real_name, realName: defenderLadder.real_name,
nickname: defenderLadder.user?.nickname, nickname: defenderLadder.user?.nickname,
avatar: defenderLadder.user?.avatar, avatar: normalizeAvatarForClient(defenderLadder.user?.avatar, req),
level: defenderLadder.level, level: defenderLadder.level,
powerScore: defenderLadder.power_score, powerScore: defenderLadder.power_score,
userId: defenderLadder.user_id, // 添加 user_id用于前端判断 userId: defenderLadder.user_id, // 添加 user_id用于前端判断

View File

@ -10,6 +10,7 @@ const {
calculateDistance, calculateDistance,
getFullUrl, getFullUrl,
normalizeAvatarUrl, normalizeAvatarUrl,
normalizeAvatarForClient,
} = require("../utils/helper"); } = require("../utils/helper");
const { LADDER_LEVEL_NAMES } = require("../config/constants"); const { LADDER_LEVEL_NAMES } = require("../config/constants");
const { Op } = require("sequelize"); const { Op } = require("sequelize");
@ -76,7 +77,7 @@ class UserController {
responseData.userInfo = { responseData.userInfo = {
id: user.id, id: user.id,
nickname: user.nickname, nickname: user.nickname,
avatar: getFullUrl(user.avatar, req), avatar: normalizeAvatarForClient(user.avatar, req),
phone: user.phone, phone: user.phone,
gender: user.gender, gender: user.gender,
memberCode: user.member_code, memberCode: user.member_code,
@ -96,7 +97,7 @@ class UserController {
? { ? {
id: user.id, id: user.id,
nickname: user.nickname, nickname: user.nickname,
avatar: user.avatar, avatar: normalizeAvatarForClient(user.avatar, req),
phone: user.phone, phone: user.phone,
gender: user.gender, gender: user.gender,
memberCode: user.member_code, memberCode: user.member_code,
@ -281,7 +282,7 @@ class UserController {
userInfo: { userInfo: {
id: user.id, id: user.id,
nickname: user.nickname, nickname: user.nickname,
avatar: getFullUrl(user.avatar, req), avatar: normalizeAvatarForClient(user.avatar, req),
phone: user.phone, phone: user.phone,
gender: user.gender, gender: user.gender,
memberCode: user.member_code, memberCode: user.member_code,
@ -373,7 +374,7 @@ class UserController {
success( success(
{ {
nickname: user.nickname, nickname: user.nickname,
avatar: getFullUrl(user.avatar, req), avatar: normalizeAvatarForClient(user.avatar, req),
gender: user.gender, gender: user.gender,
}, },
"更新成功", "更新成功",
@ -412,7 +413,7 @@ class UserController {
const result = { const result = {
id: user.id, id: user.id,
nickname: user.nickname, nickname: user.nickname,
avatar: getFullUrl(user.avatar, req), avatar: normalizeAvatarForClient(user.avatar, req),
phone: user.phone, phone: user.phone,
gender: user.gender, gender: user.gender,
memberCode: user.member_code, memberCode: user.member_code,

View File

@ -204,6 +204,25 @@ function getFullUrl(path, req) {
return `${protocol}://${host}${path}`; return `${protocol}://${host}${path}`;
} }
/**
* 规范化返回给客户端的头像URL
* 如果头像是服务端的默认头像URL返回null让前端使用本地默认头像
* @param {string} avatar - 头像URL可能是完整URL或相对路径
* @param {object} req - Express request对象用于获取host
* @returns {string|null} 规范化后的头像URL如果是默认头像则返回null
*/
function normalizeAvatarForClient(avatar, req) {
if (!avatar) return null;
// 检查是否是默认头像相对路径或完整URL
if (avatar.includes('/images/avatar-default.svg') || avatar.includes('avatar-default')) {
return null;
}
// 使用 getFullUrl 处理其他头像URL
return getFullUrl(avatar, req) || null;
}
module.exports = { module.exports = {
generateMemberCode, generateMemberCode,
generateMatchCode, generateMatchCode,
@ -215,5 +234,6 @@ module.exports = {
error, error,
pageResult, pageResult,
getFullUrl, getFullUrl,
normalizeAvatarUrl normalizeAvatarUrl,
normalizeAvatarForClient
}; };