|
|
@@ -1108,6 +1108,198 @@ export class WorkDayStatisticsService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 获取作品数据列表(用于「作品数据」页)
|
|
|
+ * 依据 work_day_statistics 进行区间汇总统计
|
|
|
+ */
|
|
|
+ async getWorksAnalytics(
|
|
|
+ userId: number,
|
|
|
+ options: {
|
|
|
+ startDate: string;
|
|
|
+ endDate: string;
|
|
|
+ platform?: string;
|
|
|
+ accountIds?: number[];
|
|
|
+ groupId?: number;
|
|
|
+ keyword?: string;
|
|
|
+ sortBy?: 'publish_desc' | 'publish_asc' | 'views_desc' | 'likes_desc' | 'comments_desc';
|
|
|
+ page?: number;
|
|
|
+ pageSize?: number;
|
|
|
+ }
|
|
|
+ ): Promise<{
|
|
|
+ summary: {
|
|
|
+ totalWorks: number;
|
|
|
+ recommendCount: number;
|
|
|
+ viewsCount: number;
|
|
|
+ commentsCount: number;
|
|
|
+ sharesCount: number;
|
|
|
+ collectsCount: number;
|
|
|
+ likesCount: number;
|
|
|
+ };
|
|
|
+ total: number;
|
|
|
+ works: Array<{
|
|
|
+ id: number;
|
|
|
+ title: string;
|
|
|
+ coverUrl: string;
|
|
|
+ platform: string;
|
|
|
+ accountId: number;
|
|
|
+ accountName: string;
|
|
|
+ accountAvatar: string | null;
|
|
|
+ workType: string;
|
|
|
+ publishTime: string | null;
|
|
|
+ recommendCount: number;
|
|
|
+ viewsCount: number;
|
|
|
+ commentsCount: number;
|
|
|
+ sharesCount: number;
|
|
|
+ collectsCount: number;
|
|
|
+ likesCount: number;
|
|
|
+ }>;
|
|
|
+ }> {
|
|
|
+ const {
|
|
|
+ startDate,
|
|
|
+ endDate,
|
|
|
+ platform,
|
|
|
+ accountIds,
|
|
|
+ groupId,
|
|
|
+ keyword,
|
|
|
+ sortBy = 'publish_desc',
|
|
|
+ page = 1,
|
|
|
+ pageSize = 20,
|
|
|
+ } = options;
|
|
|
+
|
|
|
+ const startDateStr = startDate;
|
|
|
+ const endDateStr = endDate;
|
|
|
+
|
|
|
+ // 基础查询:当前用户的作品
|
|
|
+ const qb = this.workRepository
|
|
|
+ .createQueryBuilder('w')
|
|
|
+ .leftJoin(WorkDayStatistics, 'wds', 'wds.work_id = w.id AND wds.record_date >= :wStart AND wds.record_date <= :wEnd', {
|
|
|
+ wStart: startDateStr,
|
|
|
+ wEnd: endDateStr,
|
|
|
+ })
|
|
|
+ .innerJoin(PlatformAccount, 'pa', 'pa.id = w.accountId')
|
|
|
+ .select('w.id', 'id')
|
|
|
+ .addSelect('w.title', 'title')
|
|
|
+ .addSelect('w.cover_url', 'coverUrl')
|
|
|
+ .addSelect('w.platform', 'platform')
|
|
|
+ .addSelect('w.accountId', 'accountId')
|
|
|
+ .addSelect('pa.accountName', 'accountName')
|
|
|
+ .addSelect('pa.avatarUrl', 'accountAvatar')
|
|
|
+ .addSelect('w.status', 'workType')
|
|
|
+ .addSelect('w.publish_time', 'publishTime')
|
|
|
+ .addSelect('COALESCE(SUM(wds.play_count), 0)', 'viewsCount')
|
|
|
+ .addSelect('COALESCE(SUM(wds.comment_count), 0)', 'commentsCount')
|
|
|
+ .addSelect('COALESCE(SUM(wds.share_count), 0)', 'sharesCount')
|
|
|
+ .addSelect('COALESCE(SUM(wds.collect_count), 0)', 'collectsCount')
|
|
|
+ .addSelect('COALESCE(SUM(wds.like_count), 0)', 'likesCount')
|
|
|
+ .where('w.userId = :userId', { userId });
|
|
|
+
|
|
|
+ if (platform) {
|
|
|
+ qb.andWhere('w.platform = :platform', { platform });
|
|
|
+ }
|
|
|
+ if (accountIds && accountIds.length > 0) {
|
|
|
+ qb.andWhere('w.accountId IN (:...accountIds)', { accountIds });
|
|
|
+ }
|
|
|
+ if (groupId) {
|
|
|
+ qb.andWhere('pa.groupId = :groupId', { groupId });
|
|
|
+ }
|
|
|
+ if (keyword && keyword.trim()) {
|
|
|
+ const kw = `%${keyword.trim()}%`;
|
|
|
+ qb.andWhere('(w.title LIKE :kw OR pa.accountName LIKE :kw)', { kw });
|
|
|
+ }
|
|
|
+
|
|
|
+ qb.groupBy('w.id');
|
|
|
+
|
|
|
+ // 排序:统一按发布时间倒序(最新的在前)
|
|
|
+ qb.orderBy('w.publish_time', 'DESC');
|
|
|
+
|
|
|
+ // 统计总数(作品数)
|
|
|
+ const countQb = this.workRepository
|
|
|
+ .createQueryBuilder('w')
|
|
|
+ .innerJoin(PlatformAccount, 'pa', 'pa.id = w.accountId')
|
|
|
+ .where('w.userId = :userId', { userId });
|
|
|
+
|
|
|
+ if (platform) {
|
|
|
+ countQb.andWhere('w.platform = :platform', { platform });
|
|
|
+ }
|
|
|
+ if (accountIds && accountIds.length > 0) {
|
|
|
+ countQb.andWhere('w.accountId IN (:...accountIds)', { accountIds });
|
|
|
+ }
|
|
|
+ if (groupId) {
|
|
|
+ countQb.andWhere('pa.groupId = :groupId', { groupId });
|
|
|
+ }
|
|
|
+ if (keyword && keyword.trim()) {
|
|
|
+ const kw = `%${keyword.trim()}%`;
|
|
|
+ countQb.andWhere('(w.title LIKE :kw OR pa.accountName LIKE :kw)', { kw });
|
|
|
+ }
|
|
|
+
|
|
|
+ const total = await countQb.getCount();
|
|
|
+
|
|
|
+ // 分页
|
|
|
+ const offset = (page - 1) * pageSize;
|
|
|
+ qb.skip(offset).take(pageSize);
|
|
|
+
|
|
|
+ const rows = await qb.getRawMany();
|
|
|
+
|
|
|
+ let totalViews = 0;
|
|
|
+ let totalComments = 0;
|
|
|
+ let totalShares = 0;
|
|
|
+ let totalCollects = 0;
|
|
|
+ let totalLikes = 0;
|
|
|
+
|
|
|
+ const works = rows.map((row) => {
|
|
|
+ const views = Number(row.viewsCount) || 0;
|
|
|
+ const comments = Number(row.commentsCount) || 0;
|
|
|
+ const shares = Number(row.sharesCount) || 0;
|
|
|
+ const collects = Number(row.collectsCount) || 0;
|
|
|
+ const likes = Number(row.likesCount) || 0;
|
|
|
+
|
|
|
+ totalViews += views;
|
|
|
+ totalComments += comments;
|
|
|
+ totalShares += shares;
|
|
|
+ totalCollects += collects;
|
|
|
+ totalLikes += likes;
|
|
|
+
|
|
|
+ const publishTime =
|
|
|
+ row.publishTime instanceof Date
|
|
|
+ ? row.publishTime.toISOString()
|
|
|
+ : row.publishTime
|
|
|
+ ? String(row.publishTime)
|
|
|
+ : null;
|
|
|
+
|
|
|
+ return {
|
|
|
+ id: Number(row.id),
|
|
|
+ title: row.title || '',
|
|
|
+ coverUrl: row.coverUrl || '',
|
|
|
+ platform: row.platform || '',
|
|
|
+ accountId: Number(row.accountId) || 0,
|
|
|
+ accountName: row.accountName || '',
|
|
|
+ accountAvatar: row.accountAvatar || null,
|
|
|
+ workType: row.workType || '动态',
|
|
|
+ publishTime,
|
|
|
+ recommendCount: 0,
|
|
|
+ viewsCount: views,
|
|
|
+ commentsCount: comments,
|
|
|
+ sharesCount: shares,
|
|
|
+ collectsCount: collects,
|
|
|
+ likesCount: likes,
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ summary: {
|
|
|
+ totalWorks: total,
|
|
|
+ recommendCount: 0,
|
|
|
+ viewsCount: totalViews,
|
|
|
+ commentsCount: totalComments,
|
|
|
+ sharesCount: totalShares,
|
|
|
+ collectsCount: totalCollects,
|
|
|
+ likesCount: totalLikes,
|
|
|
+ },
|
|
|
+ total,
|
|
|
+ works,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 获取平台详情数据
|
|
|
* 包括汇总统计、每日汇总数据和账号列表
|
|
|
*/
|