Selaa lähdekoodia

视频号数据

Ethanfly 20 tuntia sitten
vanhempi
commit
376ec74eef

+ 1 - 0
client/src/components.d.ts

@@ -18,6 +18,7 @@ declare module 'vue' {
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElContainer: typeof import('element-plus/es')['ElContainer']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElDrawer: typeof import('element-plus/es')['ElDrawer']

+ 6 - 0
server/src/models/entities/UserDayStatistics.ts

@@ -39,6 +39,12 @@ export class UserDayStatistics {
   @Column({ name: 'collect_count', type: 'int', default: 0, comment: '收藏数' })
   collectCount!: number;
 
+  @Column({ name: 'recommend_count', type: 'int', default: 0, comment: '推荐数' })
+  recommendCount!: number;
+
+  @Column({ name: 'follow_count', type: 'int', default: 0, comment: '关注数(来自视频等)' })
+  followCount!: number;
+
   @Column({ name: 'cover_click_rate', type: 'varchar', length: 50, default: '0', comment: '封面点击率' })
   coverClickRate!: string;
 

+ 10 - 0
server/src/services/UserDayStatisticsService.ts

@@ -12,6 +12,8 @@ export interface UserDayStatisticsItem {
   likeCount?: number;
   shareCount?: number;
   collectCount?: number;
+  recommendCount?: number;
+  followCount?: number;
   coverClickRate?: string;
   avgWatchDuration?: string;
   totalWatchDuration?: string;
@@ -73,6 +75,8 @@ export class UserDayStatisticsService {
         likeCount: item.likeCount ?? existing.likeCount,
         shareCount: item.shareCount ?? existing.shareCount,
         collectCount: item.collectCount ?? existing.collectCount,
+        recommendCount: item.recommendCount ?? existing.recommendCount,
+        followCount: item.followCount ?? existing.followCount,
         coverClickRate: item.coverClickRate ?? existing.coverClickRate ?? '0',
         avgWatchDuration: item.avgWatchDuration ?? existing.avgWatchDuration ?? '0',
         totalWatchDuration: item.totalWatchDuration ?? existing.totalWatchDuration ?? '0',
@@ -94,6 +98,8 @@ export class UserDayStatisticsService {
         likeCount: item.likeCount ?? 0,
         shareCount: item.shareCount ?? 0,
         collectCount: item.collectCount ?? 0,
+        recommendCount: item.recommendCount ?? 0,
+        followCount: item.followCount ?? 0,
         coverClickRate: item.coverClickRate ?? '0',
         avgWatchDuration: item.avgWatchDuration ?? '0',
         totalWatchDuration: item.totalWatchDuration ?? '0',
@@ -132,6 +138,8 @@ export class UserDayStatisticsService {
         likeCount: patch.likeCount ?? existing.likeCount,
         shareCount: patch.shareCount ?? existing.shareCount,
         collectCount: patch.collectCount ?? existing.collectCount,
+        recommendCount: patch.recommendCount ?? existing.recommendCount,
+        followCount: patch.followCount ?? existing.followCount,
         coverClickRate: patch.coverClickRate ?? existing.coverClickRate ?? '0',
         avgWatchDuration: patch.avgWatchDuration ?? existing.avgWatchDuration ?? '0',
         totalWatchDuration: patch.totalWatchDuration ?? existing.totalWatchDuration ?? '0',
@@ -152,6 +160,8 @@ export class UserDayStatisticsService {
       likeCount: patch.likeCount ?? 0,
       shareCount: patch.shareCount ?? 0,
       collectCount: patch.collectCount ?? 0,
+      recommendCount: patch.recommendCount ?? 0,
+      followCount: patch.followCount ?? 0,
       coverClickRate: patch.coverClickRate ?? '0',
       avgWatchDuration: patch.avgWatchDuration ?? '0',
       totalWatchDuration: patch.totalWatchDuration ?? '0',

+ 21 - 1
server/src/services/WeixinVideoDataCenterImportService.ts

@@ -223,6 +223,8 @@ async function parseWeixinVideoFile(filePath: string): Promise<Map<string, { rec
     const shareCol = colIndex(['分享', '分享量']);
     const fansIncCol = colIndex(['净增关注', '新增关注']);
     const fansTotalCol = colIndex(['关注者总数', '关注者总量', '粉丝总数', '粉丝总量']);
+    const recommendCol = colIndex(['推荐']);
+    const followCol = colIndex(['关注']);
 
     for (let i = headerLineIdx + 1; i < lines.length; i++) {
       const cols = parseCsvLine(lines[i]!).map((c) => c.replace(/^"|"$/g, '').trim());
@@ -257,6 +259,14 @@ async function parseWeixinVideoFile(filePath: string): Promise<Map<string, { rec
         const n = parseChineseNumberLike(cols[fansTotalCol]);
         if (typeof n === 'number') (obj as any).fansCount = n;
       }
+      if (recommendCol >= 0 && cols.length > recommendCol) {
+        const n = parseChineseNumberLike(cols[recommendCol]);
+        if (typeof n === 'number') (obj as any).recommendCount = n;
+      }
+      if (followCol >= 0 && cols.length > followCol) {
+        const n = parseChineseNumberLike(cols[followCol]);
+        if (typeof n === 'number') (obj as any).followCount = n;
+      }
     }
 
     return result;
@@ -299,6 +309,8 @@ async function parseWeixinVideoFile(filePath: string): Promise<Map<string, { rec
     const shareCol = colIndex(['分享', '分享量']);
     const fansIncCol = colIndex(['净增关注', '新增关注']);
     const fansTotalCol = colIndex(['关注者总数', '关注者总量', '粉丝总数', '粉丝总量']);
+    const recommendCol = colIndex(['推荐']);
+    const followCol = colIndex(['关注']);
 
     for (let i = headerIdx + 1; i < rows.length; i++) {
       const r = rows[i];
@@ -333,6 +345,14 @@ async function parseWeixinVideoFile(filePath: string): Promise<Map<string, { rec
         const n = parseChineseNumberLike(r[fansTotalCol]);
         if (typeof n === 'number') (obj as any).fansCount = n;
       }
+      if (recommendCol >= 0) {
+        const n = parseChineseNumberLike(r[recommendCol]);
+        if (typeof n === 'number') (obj as any).recommendCount = n;
+      }
+      if (followCol >= 0) {
+        const n = parseChineseNumberLike(r[followCol]);
+        if (typeof n === 'number') (obj as any).followCount = n;
+      }
     }
   }
 
@@ -487,7 +507,7 @@ export class WeixinVideoDataCenterImportService {
       await page.getByText('数据中心', { exact: false }).first().click();
       await page.waitForTimeout(800);
 
-      // 目前只需要关注者数据 + 视频数据,图文数据暂不采集
+      // 关注者数据 + 视频数据,均通过 Excel 下载
       const sections: WxSection[] = ['关注者数据', '视频数据'];
       let mergedDays = new Map<string, { recordDate: Date } & Record<string, any>>();