feat: Implement date formatting across controllers for improved readability

- Added a new utility function `formatDateTime` to standardize date formatting across various controllers.
- Updated multiple controllers (admin, article, ladder, match, points, store, user) to utilize the new formatting function for timestamps, enhancing the clarity of date and time displays in API responses.
- Ensured consistent date representation in user and match details, improving overall user experience.
This commit is contained in:
Ethanfly 2026-02-10 10:25:19 +08:00
parent b2ed278505
commit 3923217e0e
11 changed files with 66 additions and 56 deletions

View File

@ -1,7 +1,7 @@
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const { User, Store, SystemUser, LadderUser, Match, PointOrder, StoreStaff } = require('../models');
const { success, error, getPagination, pageResult } = require('../utils/helper');
const { success, error, getPagination, pageResult, formatDateTime } = require('../utils/helper');
const { Op } = require('sequelize');
class AdminController {
@ -145,7 +145,7 @@ class AdminController {
memberCode: user.member_code,
totalPoints: user.total_points,
status: user.status,
createdAt: user.created_at,
createdAt: formatDateTime(user.created_at),
// 前端根据该字段隐藏“添加为天梯用户”按钮
hasLadderUser: ladderUserIdSet.has(user.id)
})), count, page, pageSize));
@ -232,7 +232,7 @@ class AdminController {
memberCode: user.member_code,
totalPoints: user.total_points,
status: user.status,
createdAt: user.created_at,
createdAt: formatDateTime(user.created_at),
ladderUsers: user.ladderUsers.map(lu => ({
id: lu.id,
storeId: lu.store_id,
@ -382,8 +382,8 @@ class AdminController {
storeId: user.store_id,
storeName: user.store?.name,
status: user.status,
lastLoginAt: user.last_login_at,
createdAt: user.created_at
lastLoginAt: formatDateTime(user.last_login_at),
createdAt: formatDateTime(user.created_at)
})), count, page, pageSize));
} catch (err) {
console.error('获取系统用户列表失败:', err);

View File

@ -1,5 +1,5 @@
const { Article } = require('../models');
const { success, error } = require('../utils/helper');
const { success, error, formatDateTime } = require('../utils/helper');
const { Op } = require('sequelize');
class ArticleController {
@ -31,7 +31,7 @@ class ArticleController {
category: a.category,
coverImage: a.cover_image,
summary: a.summary,
createdAt: a.created_at,
createdAt: formatDateTime(a.created_at),
isTop: a.is_top === 1,
}));
@ -66,7 +66,7 @@ class ArticleController {
coverImage: article.cover_image,
summary: article.summary,
contentHtml: article.content_html,
createdAt: article.created_at,
createdAt: formatDateTime(article.created_at),
}),
);
} catch (err) {

View File

@ -1,6 +1,6 @@
const { User, LadderUser, Store } = require('../models');
const { LADDER_LEVEL_NAMES } = require('../config/constants');
const { success, error, getPagination, pageResult, generateMemberCode } = require('../utils/helper');
const { success, error, getPagination, pageResult, formatDateTime, generateMemberCode } = require('../utils/helper');
const { Op } = require('sequelize');
class LadderAdminController {
@ -63,8 +63,8 @@ class LadderAdminController {
matchCount: lu.match_count,
winCount: lu.win_count,
monthlyMatchCount: lu.monthly_match_count,
lastMatchTime: lu.last_match_time,
createdAt: lu.created_at
lastMatchTime: formatDateTime(lu.last_match_time),
createdAt: formatDateTime(lu.created_at)
})), count, page, pageSize));
} catch (err) {
console.error('获取天梯用户列表失败:', err);

View File

@ -9,6 +9,7 @@ const {
error,
getPagination,
pageResult,
formatDateTime,
} = require("../utils/helper");
const { Op } = require("sequelize");
const sequelize = require("../config/database");
@ -247,7 +248,7 @@ class LadderController {
rank: higherCount + 1,
storeId: ladderUser.store_id,
storeName: ladderUser.store?.name,
lastMatchTime: ladderUser.last_match_time,
lastMatchTime: formatDateTime(ladderUser.last_match_time),
}),
);
} catch (err) {
@ -319,7 +320,7 @@ class LadderController {
rank: higherCount + 1,
storeId: ladderUser.store_id,
storeName: ladderUser.store && ladderUser.store.name,
lastMatchTime: ladderUser.last_match_time,
lastMatchTime: formatDateTime(ladderUser.last_match_time),
}),
);
} catch (err) {

View File

@ -1,6 +1,6 @@
const { Match, MatchGame, MatchPlayer, MatchRound, LadderUser, User, Store, sequelize } = require('../models');
const { MATCH_TYPES, MATCH_STATUS, RANKING_STAGE, CONFIRM_STATUS } = require('../config/constants');
const { generateMatchCode, success, error, getPagination, pageResult } = require('../utils/helper');
const { generateMatchCode, success, error, getPagination, pageResult, formatDateTime } = require('../utils/helper');
const PowerCalculator = require('../services/powerCalculator');
const { broadcastToUsers, sendMatchNotification } = require('../websocket');
const { Op } = require('sequelize');
@ -53,9 +53,9 @@ class MatchAdminController {
refereeName: match.referee?.nickname,
status: match.status,
stage: match.stage,
startTime: match.start_time,
endTime: match.end_time,
createdAt: match.created_at
startTime: formatDateTime(match.start_time),
endTime: formatDateTime(match.end_time),
createdAt: formatDateTime(match.created_at)
})), count, page, pageSize));
} catch (err) {
console.error('获取比赛列表失败:', err);
@ -244,8 +244,8 @@ class MatchAdminController {
storeName: match.store?.name,
refereeId: match.referee_id,
refereeName: match.referee?.nickname,
startTime: match.start_time,
endTime: match.end_time,
startTime: formatDateTime(match.start_time),
endTime: formatDateTime(match.end_time),
players: sortedPlayers.map(p => ({
id: p.id,
ladderUserId: p.ladder_user_id,

View File

@ -1,6 +1,6 @@
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 { generateMatchCode, success, error, getPagination, pageResult, normalizeAvatarForClient } = require('../utils/helper');
const { generateMatchCode, success, error, getPagination, pageResult, formatDateTime, normalizeAvatarForClient } = require('../utils/helper');
const PowerCalculator = require('../services/powerCalculator');
const { sendChallengeNotification, sendScoreConfirmNotification, sendMatchNotification, broadcastToUsers, sendToUser } = require('../websocket');
const matchAdminController = require('./matchAdminController');
@ -1036,7 +1036,7 @@ class MatchController {
stageName: stageNames[match.stage] || '未知',
weight: match.weight,
storeName: match.store?.name,
startTime: match.start_time,
startTime: formatDateTime(match.start_time),
refereeId: match.referee_id,
isReferee: isReferee,
players: (match.players || []).map(p => ({
@ -1392,7 +1392,7 @@ class MatchController {
powerChange: game.winner_id === myLadderId
? game.winner_power_change
: game.loser_power_change,
confirmedAt: game.confirmed_at
confirmedAt: formatDateTime(game.confirmed_at)
};
}));
@ -1446,17 +1446,6 @@ class MatchController {
});
const opponentMap = new Map(opponents.map((o) => [String(o.id), o]));
const formatMatchTime = (date) => {
if (!date) return '';
const d = new Date(date);
const Y = d.getFullYear();
const M = String(d.getMonth() + 1).padStart(2, '0');
const D = String(d.getDate()).padStart(2, '0');
const h = String(d.getHours()).padStart(2, '0');
const m = String(d.getMinutes()).padStart(2, '0');
return `${Y}-${M}-${D} ${h}:${m}`;
};
const list = rows.map((game) => {
const isPlayer1 = game.player1_id === player.id;
const opponentId = isPlayer1 ? game.player2_id : game.player1_id;
@ -1466,7 +1455,7 @@ class MatchController {
const opponentScore = isPlayer1 ? game.player2_score : game.player1_score;
const isWin = game.winner_id === player.id;
const typeName = game.match && game.match.type === MATCH_TYPES.CHALLENGE ? '挑战赛' : '排位赛';
const timeText = formatMatchTime(game.confirmed_at);
const timeText = formatDateTime(game.confirmed_at);
return {
id: game.id,
@ -1682,8 +1671,8 @@ class MatchController {
opponent,
currentGame,
playerCount,
startTime: match.start_time,
createdAt: match.created_at,
startTime: formatDateTime(match.start_time),
createdAt: formatDateTime(match.created_at),
isReferee: match.referee_id === user.id
};
}));
@ -1745,9 +1734,9 @@ class MatchController {
match.type === MATCH_TYPES.RANKING
? ["报名中", "循环赛", "淘汰赛", "已结束"][match.stage]
: null,
startTime: match.start_time,
endTime: match.end_time,
createdAt: match.created_at,
startTime: formatDateTime(match.start_time),
endTime: formatDateTime(match.end_time),
createdAt: formatDateTime(match.created_at),
storeId: match.store?.id,
storeName: match.store?.name,
}));
@ -1801,7 +1790,7 @@ class MatchController {
opponentName: opponent?.real_name,
myScore: game.player1_id === ladderUser.id ? game.player1_score : game.player2_score,
opponentScore: game.player1_id === ladderUser.id ? game.player2_score : game.player1_score,
createdAt: game.created_at
createdAt: formatDateTime(game.created_at)
};
}));
@ -1983,8 +1972,8 @@ class MatchController {
status: match.status,
stage: match.stage,
storeName: match.store?.name,
startTime: match.start_time,
endTime: match.end_time,
startTime: formatDateTime(match.start_time),
endTime: formatDateTime(match.end_time),
challenger: challengerInfo,
defender: defenderInfo,
myRole: myRole || null,

View File

@ -12,6 +12,7 @@ const {
error,
getPagination,
pageResult,
formatDateTime,
} = require("../utils/helper");
const PowerCalculator = require("../services/powerCalculator");
const { Op } = require("sequelize");
@ -311,7 +312,7 @@ class PointsAdminController {
status: product.status,
storeId: product.store_id,
storeName: product.store?.name,
createdAt: product.created_at,
createdAt: formatDateTime(product.created_at),
})),
count,
page,
@ -458,8 +459,8 @@ class PointsAdminController {
status: order.status,
storeId: order.store_id,
storeName: order.store?.name,
createdAt: order.created_at,
verifiedAt: order.verified_at,
createdAt: formatDateTime(order.created_at),
verifiedAt: formatDateTime(order.verified_at),
})),
count,
page,
@ -501,7 +502,7 @@ class PointsAdminController {
orderId: order.id,
orderNo: order.order_no,
status: order.status,
verifiedAt: order.verified_at,
verifiedAt: formatDateTime(order.verified_at),
},
});
}
@ -556,7 +557,7 @@ class PointsAdminController {
orderId: order.id,
orderNo: order.order_no,
status: order.status,
verifiedAt: order.verified_at,
verifiedAt: formatDateTime(order.verified_at),
},
});
}

View File

@ -1,6 +1,6 @@
const { User, PointRecord, PointProduct, PointOrder, Store } = require('../models');
const { ORDER_STATUS } = require('../config/constants');
const { generateOrderNo, generateExchangeCode, success, error, getPagination, pageResult } = require('../utils/helper');
const { generateOrderNo, generateExchangeCode, success, error, getPagination, pageResult, formatDateTime } = require('../utils/helper');
const { Op } = require('sequelize');
const sequelize = require('../config/database');
const QRCode = require('qrcode');
@ -42,7 +42,7 @@ class PointsController {
consumeAmount: record.consume_amount,
storeName: record.store?.name,
remark: record.remark,
createdAt: record.created_at
createdAt: formatDateTime(record.created_at)
})), count, page, pageSize));
} catch (err) {
console.error('获取积分记录失败:', err);
@ -225,8 +225,8 @@ class PointsController {
status: order.status,
storeName: order.store?.name,
storeAddress: order.store?.address,
createdAt: order.created_at,
verifiedAt: order.verified_at
createdAt: formatDateTime(order.created_at),
verifiedAt: formatDateTime(order.verified_at)
})), count, page, pageSize));
} catch (err) {
console.error('获取订单列表失败:', err);
@ -267,8 +267,8 @@ class PointsController {
storeContact: order.store?.contact,
storeLatitude: order.store?.latitude,
storeLongitude: order.store?.longitude,
createdAt: order.created_at,
verifiedAt: order.verified_at
createdAt: formatDateTime(order.created_at),
verifiedAt: formatDateTime(order.verified_at)
}));
} catch (err) {
console.error('获取订单详情失败:', err);

View File

@ -1,5 +1,5 @@
const { Store, LadderUser } = require('../models');
const { success, error, getPagination, pageResult, calculateDistance } = require('../utils/helper');
const { success, error, getPagination, pageResult, formatDateTime, calculateDistance } = require('../utils/helper');
const { Op } = require('sequelize');
class StoreController {
@ -194,7 +194,7 @@ class StoreController {
realName: lu.real_name,
level: lu.level,
powerScore: lu.power_score,
lastMatchTime: lu.last_match_time
lastMatchTime: formatDateTime(lu.last_match_time)
}))));
} catch (err) {
console.error('获取天梯门店失败:', err);

View File

@ -8,6 +8,7 @@ const {
success,
error,
calculateDistance,
formatDateTime,
getFullUrl,
normalizeAvatarUrl,
normalizeAvatarForClient,
@ -548,7 +549,7 @@ class UserController {
matchCount: lu.match_count,
winCount: lu.win_count,
monthlyMatchCount: lu.monthly_match_count,
lastMatchTime: lu.last_match_time,
lastMatchTime: formatDateTime(lu.last_match_time),
})),
),
);

View File

@ -204,6 +204,23 @@ function getFullUrl(path, req) {
return `${protocol}://${host}${path}`;
}
/**
* 统一接口返回的时间格式YYYY-MM-DD HH:mm服务器本地时区
* @param {Date|string|null|undefined} date - 日期对象或 ISO 字符串
* @returns {string} 格式化后的时间字符串无效时返回空字符串
*/
function formatDateTime(date) {
if (date == null) return '';
const d = new Date(date);
if (Number.isNaN(d.getTime())) return '';
const Y = d.getFullYear();
const M = String(d.getMonth() + 1).padStart(2, '0');
const D = String(d.getDate()).padStart(2, '0');
const h = String(d.getHours()).padStart(2, '0');
const m = String(d.getMinutes()).padStart(2, '0');
return `${Y}-${M}-${D} ${h}:${m}`;
}
/**
* 规范化返回给客户端的头像URL
* 如果头像是服务端的默认头像URL返回null让前端使用本地默认头像
@ -233,6 +250,7 @@ module.exports = {
success,
error,
pageResult,
formatDateTime,
getFullUrl,
normalizeAvatarUrl,
normalizeAvatarForClient