Explorar el Código

修正完成率统计,总完成率直接写远程art表;2.增加done-rate的数据割接脚本migrate-done-rates

guoziyun hace 9 meses
padre
commit
7273356808
Se han modificado 2 ficheros con 148 adiciones y 8 borrados
  1. 38 8
      oms/services/cron-jobs/done-rate.ts
  2. 110 0
      oms/src/scripts/migrate-done-rates.ts

+ 38 - 8
oms/services/cron-jobs/done-rate.ts

@@ -4,12 +4,15 @@ import dayjs from "dayjs";
 import doneRateService from "../../src/services/doneRateService"; // 导入 DoneRateService
 import artService from "../../src/services/artService"; // 👈 导入 ArtService
 import { clickhouseService } from "../../src/app"; // 导入 ClickhouseService 实例
-import mongoose from "mongoose"; // 导入 mongoose 用于处理 ObjectId
+import mongoose, { Connection } from "mongoose"; // 导入 mongoose 和 Connection 用于处理远程连接
 import Art, { IArt } from "../../src/models/artModel"; // 👈 导入 Art 模型和 IArt 接口
 
 // ClickHouse 表名
 const CLICKHOUSE_EVENTS_TABLE = "events_raw"; // 确保与 ClickHouseService 中的表名一致
 
+// 远程数据库连接 URL
+const REMOTE_MONGO_URI = "mongodb://coloring:coloring123.@hk.jccytech.cn:7881/?authSource=admin";
+
 /**
  * ClickHouse 查询结果接口:每日每个作品的独立开始用户数
  */
@@ -29,7 +32,7 @@ interface ClickHouseDoneCountResult {
 /**
  * 每日统计昨天的作品完成率。
  * 统计逻辑从 ClickHouse 中提取数据,得到每个作品的完成情况,并更新到 doneRateModel。
- * 随后,根据这些日统计数据,累加更新 Art 表的总统计字段。
+ * 随后,根据这些日统计数据,累加更新本地和远程的 Art 表的总统计字段。
  * @returns Promise<string> - 返回统计结果的摘要信息。
  */
 async function run(): Promise<string> {
@@ -43,6 +46,9 @@ async function run(): Promise<string> {
 
   console.log(`[DoneRate Cron] Processing data for date: ${yesterdayYYYYMMDD}`);
 
+  let remoteConn: Connection | null = null;
+  let updatedRemoteArtworksCount = 0;
+
   try {
     // --- 1. 从 ClickHouse 中提取数据 ---
 
@@ -130,8 +136,14 @@ async function run(): Promise<string> {
 
     console.log(`[DoneRate Cron] DoneRate model update completed. Total artworks processed: ${totalProcessedArtworks}. Created: ${createdRecordsCount}, Updated: ${updatedRecordsCount}.`);
 
-    // --- 3. 获取昨天的所有 DoneRate 记录,并更新 Art 表的统计字段 ---
-    let updatedArtworksCount = 0; // for Art model
+    // --- 3. 获取昨天的所有 DoneRate 记录,并更新本地和远程的 Art 表 ---
+
+    // 建立远程数据库连接和模型
+    remoteConn = await mongoose.createConnection(REMOTE_MONGO_URI);
+    const RemoteArt = remoteConn.model<IArt>("Art", Art.schema);
+    console.log(`[DoneRate Cron] Connected to remote database.`);
+
+    let updatedLocalArtworksCount = 0; // for Art model
     const yesterdayDoneRates = await doneRateService.getDoneRatesByDate(yesterdayYYYYMMDD);
     console.log(`[DoneRate Cron] Found ${yesterdayDoneRates.length} DoneRate records for yesterday to update Art table.`);
 
@@ -148,27 +160,45 @@ async function run(): Promise<string> {
           // 重新计算总完成率
           const newCompletionRate = newTotalStartCount > 0 ? (newTotalDoneCount / newTotalStartCount) * 100 : 0;
 
-          // 更新 Art 文档
+          // **【新增】** 更新本地 Art 文档
           await artService.updateArt(artworkId.toString(), {
             totalStartCount: newTotalStartCount,
             totalDoneCount: newTotalDoneCount,
             completionRate: newCompletionRate,
           });
-          updatedArtworksCount++;
+          updatedLocalArtworksCount++;
+
+          // **【新增】** 同步更新远程 Art 文档
+          const remoteArtDoc = await RemoteArt.findById(artworkId);
+          if (remoteArtDoc) {
+            remoteArtDoc.totalStartCount = newTotalStartCount;
+            remoteArtDoc.totalDoneCount = newTotalDoneCount;
+            remoteArtDoc.completionRate = newCompletionRate;
+            await remoteArtDoc.save();
+            updatedRemoteArtworksCount++;
+          } else {
+            console.warn(`[DoneRate Cron] Remote Art document with ID ${artworkId} not found. Skipping remote update.`);
+          }
         } else {
-          console.warn(`[DoneRate Cron] Art document with ID ${artworkId} not found for DoneRate record (date: ${doneRateDoc.date}). Skipping Art update.`);
+          console.warn(`[DoneRate Cron] Local Art document with ID ${artworkId} not found for DoneRate record (date: ${doneRateDoc.date}). Skipping Art update.`);
         }
       } catch (artUpdateError) {
         console.error(`[DoneRate Cron] Error updating Art document for artwork ID ${doneRateDoc.res}:`, artUpdateError);
       }
     }
 
-    const summary = `[DoneRate Cron] Daily done-rate calculation for ${yesterdayYYYYMMDD} completed. Total DoneRate processed: ${totalProcessedArtworks}. Created DoneRate: ${createdRecordsCount}, Updated DoneRate: ${updatedRecordsCount}. Updated Art records: ${updatedArtworksCount}.`;
+    const summary = `[DoneRate Cron] Daily done-rate calculation for ${yesterdayYYYYMMDD} completed. Total DoneRate processed: ${totalProcessedArtworks}. Created DoneRate: ${createdRecordsCount}, Updated DoneRate: ${updatedRecordsCount}. Updated Local Art records: ${updatedLocalArtworksCount}. Updated Remote Art records: ${updatedRemoteArtworksCount}.`;
     console.log(summary);
     return summary;
   } catch (error) {
     console.error(`[DoneRate Cron] Error during done-rate calculation for ${yesterdayYYYYMMDD}:`, error);
     throw new Error("Failed to calculate daily done-rates."); // 抛出错误以通知 cron 调度器
+  } finally {
+    // 确保在任何情况下都关闭远程连接
+    if (remoteConn) {
+      await remoteConn.close();
+      console.log("[DoneRate Cron] Disconnected from remote database.");
+    }
   }
 }
 

+ 110 - 0
oms/src/scripts/migrate-done-rates.ts

@@ -0,0 +1,110 @@
+// oms/scripts/migrate-done-rates.ts
+
+import mongoose, { Schema, Document, Connection } from "mongoose";
+import dayjs from "dayjs";
+import customParseFormat from "dayjs/plugin/customParseFormat";
+import DoneRate from "../models/doneRateModel"; // 导入本地 DoneRate 模型
+
+dayjs.extend(customParseFormat);
+
+// --- 数据库配置 ---
+// 本地 OMS 数据库的连接字符串,请根据你的实际配置修改
+const LOCAL_MONGO_URI = "mongodb://oms:oms123.@localhost:27717/omsdb?authSource=admin";
+// 远程旧 CLOGS 数据库的连接字符串
+const REMOTE_CLOGS_MONGO_URI = "mongodb://clogs:clogs123.%23%23@localhost:27017/clogs";
+
+// --- 旧数据库 DoneRate 表的 Schema 和 Model ---
+interface IOldDoneRate extends Document {
+  _id: string; // '20221215-6066d2e9e384d9182392c9bd'
+  collectionName: string; // '20221215'
+  res: mongoose.Types.ObjectId;
+  startCount: number;
+  doneCount: number;
+  completionRate: number;
+}
+
+const oldDoneRateSchema: Schema<IOldDoneRate> = new Schema(
+  {
+    _id: String,
+    collectionName: String,
+    res: Schema.Types.ObjectId,
+    startCount: Number,
+    doneCount: Number,
+    completionRate: Number,
+  },
+  {
+    collection: "done_rate", // 指定旧的表名
+    versionKey: false,
+    _id: false, // _id 已经由旧数据提供,不自动生成
+  }
+);
+
+let localConn: Connection | null = null;
+let remoteConn: Connection | null = null;
+
+/**
+ * 迁移函数,执行数据割接
+ */
+async function migrateDoneRates() {
+  console.log("[Migration] Starting data migration from clogs.done_rate to oms.doneRates...");
+
+  try {
+    // 1. 建立本地和远程数据库连接
+    localConn = await mongoose.createConnection(LOCAL_MONGO_URI);
+    console.log("[Migration] Connected to local OMS database.");
+
+    remoteConn = await mongoose.createConnection(REMOTE_CLOGS_MONGO_URI);
+    const OldDoneRate = remoteConn.model<IOldDoneRate>("OldDoneRate", oldDoneRateSchema);
+    console.log("[Migration] Connected to remote CLOGS database.");
+
+    // 2. 从旧表中查询所有数据
+    console.log("[Migration] Fetching all records from remote 'done_rate' table...");
+    const oldRecords = await OldDoneRate.find({}).lean(); // 使用 lean() 获得原生 JS 对象,性能更高
+    console.log(`[Migration] Found ${oldRecords.length} records to migrate.`);
+
+    if (oldRecords.length === 0) {
+      console.log("[Migration] No data to migrate. Exiting.");
+      return;
+    }
+
+    // 3. 格式化数据以匹配本地模型
+    const newRecords = oldRecords.map((record) => ({
+      date: record.collectionName,
+      res: record.res,
+      startCount: record.startCount,
+      doneCount: record.doneCount,
+      completionRate: record.completionRate,
+    }));
+
+    // 4. 将格式化后的数据批量插入到本地表中
+    console.log("[Migration] Inserting records into local 'doneRates' collection...");
+    const localDoneRateModel = localConn.model("DoneRate", DoneRate.schema);
+    const result = await localDoneRateModel.insertMany(newRecords, { ordered: false });
+
+    console.log(`[Migration] Successfully migrated ${result.length} records.`);
+    console.log("[Migration] Data migration completed successfully!");
+  } catch (error: any) {
+    console.error("[Migration] An error occurred during migration:", error);
+    // 检查是否是重复键错误,这是正常的
+    if (error.code === 11000) {
+      console.warn("[Migration] Some records may already exist (duplicate key error). This is expected if the script is run multiple times.");
+    }
+    throw error; // 抛出错误以在控制台中显示
+  } finally {
+    // 5. 确保关闭所有连接
+    if (localConn) {
+      await localConn.close();
+      console.log("[Migration] Local connection closed.");
+    }
+    if (remoteConn) {
+      await remoteConn.close();
+      console.log("[Migration] Remote connection closed.");
+    }
+  }
+}
+
+// 运行迁移脚本
+migrateDoneRates().catch((err) => {
+  console.error("[Migration] Script failed to run:", err);
+  process.exit(1); // 退出并返回错误码
+});