本文档详细说明平台数据详情页(/api/work-day-statistics/platform-detail)中各项数据的计算逻辑,帮助理解为什么某些数据可能显示为 0 或 null。
文件: server/src/routes/workDayStatistics.ts
路由: GET /api/work-day-statistics/platform-detail
参数:
platform: 平台类型(必填)startDate: 开始日期(必填,格式:YYYY-MM-DD)endDate: 结束日期(必填,格式:YYYY-MM-DD)文件: server/src/services/WorkDayStatisticsService.ts
方法: getPlatformDetail(userId, platform, { startDate, endDate })
// 获取该平台的所有账号
const accounts = await this.accountRepository.find({
where: {
userId,
platform: platform as any,
},
relations: ['group'],
});
对每个账号执行以下步骤:
const works = await this.workRepository.find({
where: { accountId: account.id },
select: ['id'],
});
const workIds = works.map(w => w.id);
注意: 如果账号没有作品,workIds 为空数组,后续所有统计数据都会是 0。
核心方法: getWorkSumsAtDate(workIds, targetDate)
作用: 获取指定日期(<= targetDate)时,该账号所有作品的累计数据总和
SQL 逻辑:
SELECT
COALESCE(SUM(wds.play_count), 0) AS views,
COALESCE(SUM(wds.like_count), 0) AS likes,
COALESCE(SUM(wds.comment_count), 0) AS comments,
COALESCE(SUM(wds.collect_count), 0) AS collects
FROM work_day_statistics wds
INNER JOIN (
SELECT wds2.work_id, MAX(wds2.record_date) AS record_date
FROM work_day_statistics wds2
WHERE wds2.work_id IN (...)
AND wds2.record_date <= ?
GROUP BY wds2.work_id
) latest
ON latest.work_id = wds.work_id AND latest.record_date = wds.record_date
计算增量:
// 获取结束日期和开始日期的累计数据
const [endSums, startSums] = await Promise.all([
this.getWorkSumsAtDate(workIds, endDateStr),
this.getWorkSumsAtDate(workIds, startDateStr),
]);
// 计算增量(结束日期累计值 - 开始日期累计值)
const accountViews = endSums.views - startSums.views;
const accountComments = endSums.comments - startSums.comments;
const accountLikes = endSums.likes - startSums.likes;
关键点:
workIds 为空,getWorkSumsAtDate 返回 { views: 0, likes: 0, comments: 0, collects: 0 }getWorkSumsAtDate 也会返回 0// 获取结束日期的粉丝数(<= endDate 的最近一条记录)
const endUserStat = await this.userDayStatisticsRepository
.createQueryBuilder('uds')
.where('uds.account_id = :accountId', { accountId: account.id })
.andWhere('DATE(uds.record_date) <= :d', { d: endDateStr })
.orderBy('uds.record_date', 'DESC')
.getOne();
// 获取开始日期的粉丝数(<= startDate 的最近一条记录)
const startUserStat = await this.userDayStatisticsRepository
.createQueryBuilder('uds')
.where('uds.account_id = :accountId', { accountId: account.id })
.andWhere('DATE(uds.record_date) <= :d', { d: startDateStr })
.orderBy('uds.record_date', 'DESC')
.getOne();
// 计算粉丝增量
const accountFansIncrease = (endUserStat?.fansCount || 0) - (startUserStat?.fansCount || 0);
关键点:
endUserStat 或 startUserStat 为 null,使用 || 0 默认值// 获取时间范围内的最新更新时间
const latestUserStat = await this.userDayStatisticsRepository
.createQueryBuilder('uds')
.where('uds.account_id = :accountId', { accountId: account.id })
.andWhere('DATE(uds.record_date) >= :startDate', { startDate: startDateStr })
.andWhere('DATE(uds.record_date) <= :endDate', { endDate: endDateStr })
.orderBy('uds.updated_at', 'DESC')
.getOne();
const updateTime = latestUserStat?.updatedAt
? this.formatUpdateTime(latestUserStat.updatedAt)
: '';
关键点:
user_day_statistics 记录时才有值""accountList.push({
id: account.id,
nickname: account.accountName || '',
username: account.accountId || '',
avatarUrl: account.avatarUrl,
platform: account.platform,
income: null, // 收益数据需要从其他表获取
recommendationCount: null, // 推荐量(部分平台支持)
viewsCount: accountViews > 0 ? accountViews : null, // ⚠️ 关键:只有 > 0 才返回,否则为 null
commentsCount: Math.max(0, accountComments), // ⚠️ 关键:负数会被截断为 0
likesCount: Math.max(0, accountLikes), // ⚠️ 关键:负数会被截断为 0
fansIncrease: Math.max(0, accountFansIncrease), // ⚠️ 关键:负数会被截断为 0
updateTime,
});
关键逻辑:
viewsCount:
accountViews > 0,返回实际值accountViews <= 0,返回 nullcommentsCount / likesCount / fansIncrease:
Math.max(0, value) 确保不为负数// 遍历日期范围内的每一天
while (currentDate <= dateEnd) {
const dateStr = this.formatDate(currentDate);
// 获取该日期的累计数据
const [daySums, prevDaySums] = await Promise.all([
this.getWorkSumsAtDate(workIds, dateStr),
this.getWorkSumsAtDate(workIds, this.formatDate(new Date(currentDate.getTime() - 24 * 60 * 60 * 1000))),
]);
// 计算当日增量
const dayViews = daySums.views - prevDaySums.views;
const dayComments = daySums.comments - prevDaySums.comments;
const dayLikes = daySums.likes - prevDaySums.likes;
// 获取粉丝增量
const dayUserStat = await this.userDayStatisticsRepository
.createQueryBuilder('uds')
.where('uds.account_id = :accountId', { accountId: account.id })
.andWhere('DATE(uds.record_date) = :d', { d: dateStr })
.getOne();
const prevDayUserStat = await this.userDayStatisticsRepository
.createQueryBuilder('uds')
.where('uds.account_id = :accountId', { accountId: account.id })
.andWhere('DATE(uds.record_date) = :d', { d: this.formatDate(new Date(currentDate.getTime() - 24 * 60 * 60 * 1000)) })
.getOne();
const dayFans = (dayUserStat?.fansCount || 0) - (prevDayUserStat?.fansCount || 0);
// 累加到每日汇总
if (!dailyMap.has(dateStr)) {
dailyMap.set(dateStr, {
views: 0,
comments: 0,
likes: 0,
fansIncrease: 0,
});
}
const daily = dailyMap.get(dateStr)!;
daily.views += Math.max(0, dayViews);
daily.comments += Math.max(0, dayComments);
daily.likes += Math.max(0, dayLikes);
daily.fansIncrease += Math.max(0, dayFans);
currentDate.setDate(currentDate.getDate() + 1);
}
关键点:
Math.max(0, ...) 截断为 0// 累加所有账号的数据
totalAccounts++;
totalViews += Math.max(0, accountViews);
totalComments += Math.max(0, accountComments);
totalLikes += Math.max(0, accountLikes);
totalFansIncrease += Math.max(0, accountFansIncrease);
原因:
accountViews <= 0 时返回 nullworkIds 为空)解决方案:
work_day_statistics 表中是否有该账号作品的数据原因:
Math.max(0, value) 截断为 0解决方案:
原因:
user_day_statistics 记录解决方案:
原因:
null解决方案:
作品统计数据: work_day_statistics 表
work_id, record_date, play_count, like_count, comment_count, collect_count账号统计数据: user_day_statistics 表
account_id, record_date, fans_count, updated_at账号表: platform_accounts 表
id, account_name, account_id, avatar_url, platform作品表: works 表
id, account_id账号 (platform_accounts)
↓
作品 (works) → 作品ID列表 (workIds)
↓
作品统计数据 (work_day_statistics) → 累计数据 (getWorkSumsAtDate)
↓
增量计算 (endSums - startSums) → 账号数据 (viewsCount, commentsCount, likesCount)
↓
账号统计数据 (user_day_statistics) → 粉丝增量 (fansIncrease)
SELECT COUNT(*) FROM works WHERE account_id = ?;
SELECT COUNT(*) FROM work_day_statistics wds
INNER JOIN works w ON wds.work_id = w.id
WHERE w.account_id = ?;
SELECT COUNT(*) FROM user_day_statistics
WHERE account_id = ?;
-- 检查作品统计数据
SELECT * FROM work_day_statistics wds
INNER JOIN works w ON wds.work_id = w.id
WHERE w.account_id = ?
AND DATE(wds.record_date) = '2026-01-27';
-- 检查账号统计数据
SELECT * FROM user_day_statistics
WHERE account_id = ?
AND DATE(record_date) = '2026-01-27';
当前逻辑:
viewsCount: accountViews > 0 ? accountViews : null,
建议:
// 如果账号有作品,即使增量为 0 也显示 0,而不是 null
viewsCount: workIds.length > 0 ? (accountViews > 0 ? accountViews : 0) : null,
当前逻辑:
fansIncrease: Math.max(0, accountFansIncrease),
建议:
// 如果需要显示掉粉(负数),可以改为:
fansIncrease: accountFansIncrease, // 允许负数
当前逻辑:
const updateTime = latestUserStat?.updatedAt
? this.formatUpdateTime(latestUserStat.updatedAt)
: '';
建议:
// 如果没有统计数据,可以使用账号表的更新时间
const updateTime = latestUserStat?.updatedAt
? this.formatUpdateTime(latestUserStat.updatedAt)
: (account.updatedAt ? this.formatUpdateTime(account.updatedAt) : '');
server/src/services/WorkDayStatisticsService.ts - 数据计算逻辑server/src/routes/workDayStatistics.ts - API 路由client/src/views/Analytics/PlatformDetail/index.vue - 前端展示2026-01-28