guoziyun vor 8 Monaten
Ursprung
Commit
8f7465cd26

+ 0 - 90
oms/dist/services/cron-jobs/backfill-done-rate-tip-count.js

@@ -1,90 +0,0 @@
-"use strict";
-var __importDefault = (this && this.__importDefault) || function (mod) {
-    return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-const dayjs_1 = __importDefault(require("dayjs"));
-const clients_1 = require("../../src/services/clients");
-const mongoose_1 = __importDefault(require("mongoose"));
-const doneRateModel_1 = __importDefault(require("../../src/models/doneRateModel"));
-// 导入 DoneRate 模型
-// ClickHouse 表名
-const CLICKHOUSE_EVENTS_TABLE = "events";
-// =========================================================================
-// !!! 必须修改这里以设置回填的起始日期 (YYYY-MM-DD 格式) !!!
-// 示例: 如果您的 tipCount 追踪是从 2024 年 1 月 1 日开始的,则设置为 "2024-01-01"
-const BACKFILL_START_DATE = "2025-01-03";
-/**
- * 遍历指定日期范围,从 ClickHouse 提取 tipCount 并回填到 DoneRate 记录中。
- * @returns Promise<string> - 返回回填结果的摘要信息。
- */
-async function runBackfill() {
-    console.log(`[TipCount Backfill] Starting tipCount initialization for DoneRate model.`);
-    // 设置日期范围:从 BACKFILL_START_DATE 到昨天
-    let startDate = (0, dayjs_1.default)(BACKFILL_START_DATE).startOf("day");
-    const endDate = (0, dayjs_1.default)().subtract(1, "day").startOf("day");
-    if (!startDate.isValid() || startDate.isAfter(endDate)) {
-        const errorMsg = `[TipCount Backfill] Invalid start date ${BACKFILL_START_DATE} or date range is empty. Aborting.`;
-        console.error(errorMsg);
-        return errorMsg;
-    }
-    console.log(`[TipCount Backfill] Date Range: ${startDate.format("YYYY-MM-DD")} to ${endDate.format("YYYY-MM-DD")}`);
-    let totalDaysProcessed = 0;
-    let totalDoneRateUpdated = 0;
-    // 循环遍历历史数据中的每一天
-    for (let currentDay = startDate; currentDay.isBefore(endDate) || currentDay.isSame(endDate); currentDay = currentDay.add(1, "day")) {
-        const currentYYYYMMDD = currentDay.format("YYYYMMDD");
-        const currentStartString = currentDay.format("YYYY-MM-DD HH:mm:ss");
-        const currentEndString = currentDay.endOf("day").format("YYYY-MM-DD HH:mm:ss");
-        console.log(`\n--- [TipCount Backfill] Processing date: ${currentYYYYMMDD} ---`);
-        totalDaysProcessed++;
-        try {
-            // 1. 从 ClickHouse 中提取当天的 tipCount
-            const tipCountsQuery = `
-        SELECT
-            res,
-            count() AS tip_count
-        FROM ${CLICKHOUSE_EVENTS_TABLE}
-        WHERE event = 'color_tip'
-          AND time >= toDateTime('${currentStartString}')
-          AND time < toDateTime('${currentEndString}')
-        GROUP BY res
-        HAVING res IS NOT NULL
-      `;
-            const tipResults = await clients_1.clickhouseService.queryEvents(tipCountsQuery);
-            console.log(`[TipCount Backfill] Retrieved ${tipResults.length} records with 'color_tip' event.`);
-            if (tipResults.length === 0) {
-                // 如果当天没有 tip 事件,但可能存在 DoneRate 记录,我们仍然需要更新它们 (如果它们是 null 或 undefined)
-                // 但由于 DoneRateModel 默认 tipCount: 0,所以我们只需要处理有 tip 事件的记录。
-                console.log(`[TipCount Backfill] No 'color_tip' events found for ${currentYYYYMMDD}. Skipping MongoDB update.`);
-                continue;
-            }
-            // 2. 批量更新 MongoDB 中的 DoneRate 记录
-            const bulkOps = tipResults
-                .filter((row) => row.res && mongoose_1.default.Types.ObjectId.isValid(row.res)) // 过滤掉无效的 res ID
-                .map((row) => ({
-                updateOne: {
-                    // 查找条件:日期 + 作品ID (唯一复合索引)
-                    filter: { date: currentYYYYMMDD, res: new mongoose_1.default.Types.ObjectId(row.res) },
-                    // 更新操作:设置 tipCount 字段
-                    // 注意:我们只更新 tipCount,不影响 startCount 和 doneCount
-                    update: { $set: { tipCount: row.tip_count } },
-                    // 如果 DoneRate 记录不存在,则忽略。因为 DoneRate 记录通常由 color_start/color_done 事件创建。
-                    upsert: false,
-                },
-            }));
-            if (bulkOps.length > 0) {
-                const updateResult = await doneRateModel_1.default.bulkWrite(bulkOps, { ordered: false });
-                const modifiedCount = updateResult.modifiedCount || 0;
-                totalDoneRateUpdated += modifiedCount;
-                console.log(`[TipCount Backfill] Successfully updated ${modifiedCount} DoneRate documents for ${currentYYYYMMDD}.`);
-            }
-        }
-        catch (error) {
-            console.error(`[TipCount Backfill] Fatal error processing date ${currentYYYYMMDD}:`, error);
-        }
-    }
-    const summary = `[TipCount Backfill] DoneRate tipCount backfill completed. Total days processed: ${totalDaysProcessed}. Total DoneRate documents updated: ${totalDoneRateUpdated}.`;
-    console.log(`\n${summary}`);
-    return summary;
-}
-module.exports = { runBackfill };

+ 0 - 164
oms/dist/services/cron-jobs/done-rate2.js

@@ -1,164 +0,0 @@
-"use strict";
-// oms/services/cron-jobs/done-rate.ts
-var __importDefault = (this && this.__importDefault) || function (mod) {
-    return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-const dayjs_1 = __importDefault(require("dayjs"));
-const doneRateService_1 = __importDefault(require("../../src/services/doneRateService")); // 导入 DoneRateService
-const clients_1 = require("../../src/services/clients");
-const mongoose_1 = __importDefault(require("mongoose")); // 导入 mongoose 和 Connection 用于处理远程连接
-const totalDoneRateModel_1 = __importDefault(require("../../src/models/totalDoneRateModel")); // 导入新的 TotalDoneRate 模型
-// ClickHouse 表名
-const CLICKHOUSE_EVENTS_TABLE = "events"; // 确保与 ClickHouseService 中的表名一致
-/**
- * 每日统计昨天的作品完成率。
- * 统计逻辑从 ClickHouse 中提取数据,得到每个作品的完成情况,并更新到 doneRateModel。
- * 随后,根据这些日统计数据,累加更新本地的 totalDoneRate 表。
- * @returns Promise<string> - 返回统计结果的摘要信息。
- */
-async function run() {
-    console.log("[DoneRate Cron] Starting daily done-rate calculation for yesterday..."); // 获取昨天和今天的日期
-    // 获取昨天和今天的日期
-    const yesterday = (0, dayjs_1.default)().subtract(1, "day");
-    const yesterdayYYYYMMDD = yesterday.format("YYYYMMDD");
-    const yesterdayStart = yesterday.startOf("day").toDate();
-    const yesterdayEnd = yesterday.endOf("day").toDate();
-    // 格式化日期字符串,使其符合 ClickHouse 的 toDateTime() 函数要求
-    const yesterdayStartString = (0, dayjs_1.default)(yesterdayStart).format("YYYY-MM-DD HH:mm:ss");
-    const yesterdayEndString = (0, dayjs_1.default)(yesterdayEnd).format("YYYY-MM-DD HH:mm:ss");
-    console.log(`[DoneRate Cron] Processing data for date: ${yesterdayYYYYMMDD}`);
-    try {
-        // --- 1. 从 ClickHouse 中提取数据 ---
-        // 查询昨天每个作品的独立开始用户数
-        const startCountsQuery = `
-      SELECT
-          res,
-          count(DISTINCT uid) AS unique_starts
-      FROM ${CLICKHOUSE_EVENTS_TABLE}
-      WHERE event = 'color_start'
-        AND time >= toDateTime('${yesterdayStartString}')
-        AND time < toDateTime('${yesterdayEndString}')
-      GROUP BY res
-      HAVING res IS NOT NULL
-    `;
-        const startResults = await clients_1.clickhouseService.queryEvents(startCountsQuery);
-        const artworkStartCounts = new Map();
-        startResults.forEach((row) => {
-            if (row.res && mongoose_1.default.Types.ObjectId.isValid(row.res)) {
-                artworkStartCounts.set(row.res, row.unique_starts);
-            }
-            else {
-                console.warn(`[DoneRate Cron] Invalid artwork ID found in start_counts result: ${row.res}`);
-            }
-        });
-        console.log(`[DoneRate Cron] Retrieved ${startResults.length} unique start counts from ClickHouse.`); // 查询昨天每个作品的独立完成用户数
-        // 查询昨天每个作品的独立完成用户数
-        const doneCountsQuery = `
-      SELECT
-          res,
-          count(DISTINCT uid) AS unique_dones
-      FROM ${CLICKHOUSE_EVENTS_TABLE}
-      WHERE event = 'color_done'
-        AND time >= toDateTime('${yesterdayStartString}')
-        AND time < toDateTime('${yesterdayEndString}')
-      GROUP BY res
-      HAVING res IS NOT NULL
-    `;
-        const doneResults = await clients_1.clickhouseService.queryEvents(doneCountsQuery);
-        const artworkDoneCounts = new Map();
-        doneResults.forEach((row) => {
-            if (row.res && mongoose_1.default.Types.ObjectId.isValid(row.res)) {
-                artworkDoneCounts.set(row.res, row.unique_dones);
-            }
-            else {
-                console.warn(`[DoneRate Cron] Invalid artwork ID found in done_counts result: ${row.res}`);
-            }
-        });
-        console.log(`[DoneRate Cron] Retrieved ${doneResults.length} unique done counts from ClickHouse.`);
-        // --- 2. 合并数据并更新 DoneRate 模型 ---
-        let updatedRecordsCount = 0; // for DoneRate
-        let createdRecordsCount = 0; // for DoneRate
-        // 遍历所有有开始事件的作品ID
-        for (const [resIdStr, startCount] of artworkStartCounts.entries()) {
-            const doneCount = artworkDoneCounts.get(resIdStr) || 0;
-            const resObjectId = new mongoose_1.default.Types.ObjectId(resIdStr);
-            // 使用 DoneRateService 来创建或更新记录
-            const doneRateDoc = await doneRateService_1.default.createOrUpdateDoneRate(yesterdayYYYYMMDD, resObjectId, startCount, doneCount);
-            if (doneRateDoc.createdAt.getTime() === doneRateDoc.updatedAt.getTime()) {
-                createdRecordsCount++;
-            }
-            else {
-                updatedRecordsCount++;
-            }
-            artworkDoneCounts.delete(resIdStr); // 已经处理过的作品ID从 doneCounts 中移除
-        }
-        // 处理只有完成事件但没有开始事件的作品 (通常不应发生,但以防万一)
-        for (const [resIdStr, doneCount] of artworkDoneCounts.entries()) {
-            const startCount = 0; // 没有开始事件,所以开始次数为0
-            const resObjectId = new mongoose_1.default.Types.ObjectId(resIdStr);
-            const doneRateDoc = await doneRateService_1.default.createOrUpdateDoneRate(yesterdayYYYYMMDD, resObjectId, startCount, doneCount);
-            if (doneRateDoc.createdAt.getTime() === doneRateDoc.updatedAt.getTime()) {
-                createdRecordsCount++;
-            }
-            else {
-                updatedRecordsCount++;
-            }
-        }
-        const totalProcessedArtworks = createdRecordsCount + updatedRecordsCount;
-        console.log(`[DoneRate Cron] DoneRate model update completed. Total artworks processed: ${totalProcessedArtworks}. Created: ${createdRecordsCount}, Updated: ${updatedRecordsCount}.`); // --- 3. 获取昨天的所有 DoneRate 记录,并更新本地的 totalDoneRate 表 ---
-        let updatedTotalDoneRateCount = 0;
-        const yesterdayDoneRates = await doneRateService_1.default.getDoneRatesByDate(yesterdayYYYYMMDD);
-        const totalDoneRatesToUpdate = yesterdayDoneRates.length;
-        let updateStartTime = new Date().getTime();
-        console.log(`[DoneRate Cron] 开始更新本地 TotalDoneRate 表。总计 ${totalDoneRatesToUpdate} 条记录。`);
-        for (let i = 0; i < totalDoneRatesToUpdate; i++) {
-            const doneRateDoc = yesterdayDoneRates[i];
-            try {
-                const artworkId = doneRateDoc.res; // 获取作品 ObjectId
-                // 查找现有的 TotalDoneRate 文档
-                const existingTotal = await totalDoneRateModel_1.default.findById(artworkId);
-                let newTotalStartCount = doneRateDoc.startCount;
-                let newTotalDoneCount = doneRateDoc.doneCount;
-                if (existingTotal) {
-                    // 如果已存在,则累加
-                    newTotalStartCount += existingTotal.totalStartCount || 0;
-                    newTotalDoneCount += existingTotal.totalDoneCount || 0;
-                }
-                const newCompletionRate = newTotalStartCount > 0 ? (newTotalDoneCount / newTotalStartCount) * 100 : 0;
-                // 使用 findByIdAndUpdate 进行原子操作,确保数据一致性
-                // 这里的 _id 是根据 artworkId 来查找或创建的
-                const updatedDoc = await totalDoneRateModel_1.default.findByIdAndUpdate(artworkId, {
-                    $set: {
-                        totalStartCount: newTotalStartCount,
-                        totalDoneCount: newTotalDoneCount,
-                        completionRate: newCompletionRate,
-                    },
-                }, { new: true, upsert: true } // upsert: true 表示如果文档不存在则创建
-                );
-                if (updatedDoc) {
-                    updatedTotalDoneRateCount++;
-                }
-                // 进度日志
-                if ((i + 1) % 50 === 0 || i === totalDoneRatesToUpdate - 1) {
-                    const elapsed = new Date().getTime() - updateStartTime;
-                    console.log(`[DoneRate Cron] 进度: 已更新 ${i + 1}/${totalDoneRatesToUpdate} 条本地 TotalDoneRate 记录, 当前耗时: ${elapsed}ms`);
-                }
-            }
-            catch (totalUpdateError) {
-                console.error(`[DoneRate Cron] Error updating TotalDoneRate document for artwork ID ${doneRateDoc.res}:`, totalUpdateError);
-            }
-        }
-        const updateTimeTaken = new Date().getTime() - updateStartTime;
-        console.log(`[DoneRate Cron] 本地 TotalDoneRate 表更新完成。总计更新 ${updatedTotalDoneRateCount} 条记录,总耗时 ${updateTimeTaken}ms。`);
-        const summary = `[DoneRate Cron] Daily done-rate calculation for ${yesterdayYYYYMMDD} completed. Total DoneRate processed: ${totalProcessedArtworks}. Created DoneRate: ${createdRecordsCount}, Updated DoneRate: ${updatedRecordsCount}. Updated TotalDoneRate records: ${updatedTotalDoneRateCount}.`;
-        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 {
-    }
-}
-module.exports = { run };

+ 0 - 39
oms/dist/services/cron-jobs/initialize-tip-count.js

@@ -1,39 +0,0 @@
-"use strict";
-var __importDefault = (this && this.__importDefault) || function (mod) {
-    return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-const doneRateModel_1 = __importDefault(require("../../src/models/doneRateModel"));
-/**
- * 初始化所有 DoneRate 历史文档中缺失的 tipCount 字段为 0。
- * 确保所有文档在开始回填前拥有 tipCount 字段,以保证数据一致性。
- * @returns Promise<string> - 返回初始化结果的摘要信息。
- */
-async function runInitialization() {
-    console.log("[TipCount Initialization] Starting to set missing tipCount field to 0 for all historical DoneRate documents...");
-    const startTime = Date.now();
-    try {
-        // 查找条件: 查找 tipCount 字段不存在的文档 (即历史数据)
-        const filter = { tipCount: { $exists: false } };
-        // 更新操作: 将 tipCount 设置为 0
-        const update = { $set: { tipCount: 0 } };
-        // 选项: 更新所有匹配的文档
-        const options = { multi: true };
-        const result = await doneRateModel_1.default.updateMany(filter, update, options).exec();
-        const endTime = Date.now();
-        const timeTaken = ((endTime - startTime) / 1000).toFixed(2);
-        if (result.modifiedCount === 0) {
-            console.log("[TipCount Initialization] No missing tipCount fields found. Historical data seems consistent or already initialized.");
-        }
-        else {
-            console.log(`[TipCount Initialization] Successfully initialized ${result.modifiedCount} historical DoneRate documents to tipCount: 0.`);
-        }
-        const summary = `[TipCount Initialization] Initialization completed in ${timeTaken} seconds. Total documents modified: ${result.modifiedCount}.`;
-        console.log(`\n${summary}`);
-        return summary;
-    }
-    catch (error) {
-        console.error("[TipCount Initialization] Fatal error during initialization:", error);
-        throw new Error("Failed to initialize DoneRate tipCount field.");
-    }
-}
-module.exports = { runInitialization };

+ 0 - 88
oms/dist/services/cron-jobs/recalculate-total-done-rate.js

@@ -1,88 +0,0 @@
-"use strict";
-var __importDefault = (this && this.__importDefault) || function (mod) {
-    return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-const doneRateModel_1 = __importDefault(require("../../src/models/doneRateModel"));
-const totalDoneRateModel_1 = __importDefault(require("../../src/models/totalDoneRateModel"));
-/**
- * 重新计算 TotalDoneRate 表中的所有累计字段。
- * 通过聚合 DoneRate 表中的所有历史数据来实现。
- * 此脚本应在 DoneRate 表的 tipCount 字段回填完成后运行。
- * @returns Promise<string> - 返回累计更新结果的摘要信息。
- */
-async function runRecalculation() {
-    console.log("[TotalDoneRate Recalculation] Starting full recalculation of TotalDoneRate table...");
-    const startTime = Date.now();
-    try {
-        // 1. 使用聚合操作,从 DoneRate 表中计算每个作品的总累计数据
-        console.log("[TotalDoneRate Recalculation] Step 1: Aggregating all DoneRate records...");
-        // 聚合管道定义
-        const aggregationPipeline = [
-            {
-                // 1. 按作品 ID (res) 分组
-                $group: {
-                    _id: "$res", // 使用 res 作为分组键
-                    totalStartCount: { $sum: "$startCount" },
-                    totalDoneCount: { $sum: "$doneCount" },
-                    totalTipCount: { $sum: "$tipCount" }, // 累加 tipCount
-                },
-            },
-            {
-                // 2. 计算最终的 completionRate
-                $project: {
-                    _id: "$_id",
-                    totalStartCount: "$totalStartCount",
-                    totalDoneCount: "$totalDoneCount",
-                    totalTipCount: "$totalTipCount",
-                    completionRate: {
-                        $cond: [
-                            { $gt: ["$totalStartCount", 0] },
-                            { $multiply: [{ $divide: ["$totalDoneCount", "$totalStartCount"] }, 100] },
-                            0, // 如果 totalStartCount 为 0,则 completionRate 为 0
-                        ],
-                    },
-                },
-            },
-        ];
-        const aggregatedResults = await doneRateModel_1.default.aggregate(aggregationPipeline).exec();
-        console.log(`[TotalDoneRate Recalculation] Aggregation complete. Found ${aggregatedResults.length} unique artworks to update.`);
-        // 2. 批量更新 TotalDoneRate 表
-        const bulkOps = aggregatedResults.map((result) => ({
-            updateOne: {
-                // 查找条件:使用聚合结果的 _id (即作品 ObjectId)
-                filter: { _id: result._id },
-                // 更新操作:设置所有累计字段
-                update: {
-                    $set: {
-                        totalStartCount: result.totalStartCount,
-                        totalDoneCount: result.totalDoneCount,
-                        totalTipCount: result.totalTipCount, // 设置重新计算的总道具使用数
-                        completionRate: result.completionRate,
-                    },
-                },
-                // 如果 TotalDoneRate 记录不存在,则创建新记录
-                upsert: true,
-            },
-        }));
-        if (bulkOps.length > 0) {
-            console.log(`[TotalDoneRate Recalculation] Step 2: Running bulk update for ${bulkOps.length} documents...`);
-            const updateResult = await totalDoneRateModel_1.default.bulkWrite(bulkOps, { ordered: false });
-            const modifiedCount = (updateResult.modifiedCount || 0) + (updateResult.upsertedCount || 0);
-            const endTime = Date.now();
-            const timeTaken = ((endTime - startTime) / 1000).toFixed(2);
-            const summary = `[TotalDoneRate Recalculation] Full recalculation completed in ${timeTaken} seconds. Total artworks updated/created: ${modifiedCount}.`;
-            console.log(`\n${summary}`);
-            return summary;
-        }
-        else {
-            const summary = "[TotalDoneRate Recalculation] Aggregation returned no results. TotalDoneRate table update skipped.";
-            console.log(summary);
-            return summary;
-        }
-    }
-    catch (error) {
-        console.error("[TotalDoneRate Recalculation] Fatal error during full recalculation:", error);
-        throw new Error("Failed to recalculate TotalDoneRate history.");
-    }
-}
-module.exports = { runRecalculation };

+ 0 - 537
oms/dist/src/services/messageRecordService copy.js

@@ -1,537 +0,0 @@
-"use strict";
-// oms/src/services/messageRecordService.ts
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.MessageRecordService = void 0;
-const messageRecordModel_1 = require("../models/messageRecordModel");
-class MessageRecordService {
-    /**
-     * Creates a new message push record.
-     * @param recordData Message record data.
-     * @returns The newly created message record object.
-     */
-    async createMessageRecord(recordData) {
-        const newRecord = new messageRecordModel_1.MessageRecord(recordData);
-        return await newRecord.save();
-    }
-    /**
-     * Gets paginated message records with filtering and sorting support.
-     * @param page Page number.
-     * @param limit Number of records per page.
-     * @param filters Filter conditions.
-     * @param sortField Field to sort by.
-     * @param sortOrder Sort order ('asc' or 'desc').
-     * @returns An object containing records and the total count.
-     */
-    async getPaginatedRecords(page = 1, limit = 10, filters = {}, sortField = "createdAt", sortOrder = "desc") {
-        // Build the query conditions
-        const query = {};
-        if (filters.uid) {
-            query.uid = filters.uid;
-        }
-        if (filters.activityName) {
-            query.activityName = filters.activityName;
-        }
-        if (filters.templateName) {
-            query.templateName = filters.templateName;
-        }
-        if (filters.status !== undefined) {
-            query.status = filters.status;
-        }
-        const sort = {};
-        sort[sortField] = sortOrder === "asc" ? 1 : -1;
-        const skip = (page - 1) * limit;
-        const records = await messageRecordModel_1.MessageRecord.find(query).sort(sort).skip(skip).limit(limit);
-        const total = await messageRecordModel_1.MessageRecord.countDocuments(query);
-        return { records, total };
-    }
-    /**
-     * Gets all message records for a specific user UID.
-     * @param uid User UID.
-     * @returns A list of message records.
-     */
-    async getRecordsByUid(uid) {
-        return await messageRecordModel_1.MessageRecord.find({ uid }).sort({ createdAt: -1 });
-    }
-    /**
-     * Gets all message records related to a specific activity ID.
-     * @param activityId Message activity ID.
-     * @returns A list of message records.
-     */
-    async getRecordsByActivityId(activityId) {
-        return await messageRecordModel_1.MessageRecord.find({ activityId }).sort({ createdAt: -1 });
-    }
-    /**
-     * Gets a single message record by ID.
-     * @param recordId Message record ID.
-     * @returns The message record object or null.
-     */
-    async getSingleRecord(recordId) {
-        return await messageRecordModel_1.MessageRecord.findById(recordId);
-    }
-    /**
-     * Updates the status of a message record.
-     * @param recordId Message record ID.
-     * @param updateData Data to update.
-     * @returns The updated message record object.
-     */
-    async updateMessageRecord(recordId, updateData) {
-        return await messageRecordModel_1.MessageRecord.findByIdAndUpdate(recordId, updateData, { new: true });
-    }
-    /**
-     * Gets overall message push statistics.
-     */
-    async getOverallStatistics() {
-        try {
-            const result = await messageRecordModel_1.MessageRecord.aggregate([
-                // 1. Group messages by status
-                {
-                    $group: {
-                        _id: "$status",
-                        count: { $sum: 1 },
-                    },
-                },
-                // 2. Transform the result into a more manageable format
-                {
-                    $group: {
-                        _id: null,
-                        total_records: { $sum: "$count" },
-                        status_counts: {
-                            $push: {
-                                k: { $toString: "$_id" },
-                                v: "$count",
-                            },
-                        },
-                    },
-                },
-                // 3. Reformat the output and calculate all rates
-                {
-                    $project: {
-                        _id: 0,
-                        totalRecords: "$total_records",
-                        sent: {
-                            $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0],
-                        },
-                        delivered: {
-                            $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0],
-                        },
-                        opened: {
-                            $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0],
-                        },
-                        failed: {
-                            $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "-1"] }] }, 0],
-                        },
-                        // Sent success rate = (sent + delivered + opened) / total
-                        sentSuccessRate: {
-                            $cond: [
-                                { $eq: ["$total_records", 0] },
-                                0,
-                                {
-                                    $divide: [
-                                        {
-                                            $sum: [
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0] },
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
-                                            ],
-                                        },
-                                        "$total_records",
-                                    ],
-                                },
-                            ],
-                        },
-                        // Delivered rate = delivered / (sent + delivered)
-                        deliveredRate: {
-                            $cond: [
-                                {
-                                    $eq: [
-                                        {
-                                            $sum: [
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0] },
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
-                                            ],
-                                        },
-                                        0,
-                                    ],
-                                },
-                                0,
-                                {
-                                    $divide: [
-                                        { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
-                                        {
-                                            $sum: [
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0] },
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
-                                            ],
-                                        },
-                                    ],
-                                },
-                            ],
-                        },
-                        // Opened rate = opened / (delivered + opened)
-                        openedRate: {
-                            $cond: [
-                                {
-                                    $eq: [
-                                        {
-                                            $sum: [
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
-                                            ],
-                                        },
-                                        0,
-                                    ],
-                                },
-                                0,
-                                {
-                                    $divide: [
-                                        { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
-                                        {
-                                            $sum: [
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
-                                                { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
-                                            ],
-                                        },
-                                    ],
-                                },
-                            ],
-                        },
-                        // Token invalidation rate
-                        tokenInvalidationRate: {
-                            $cond: [{ $eq: ["$total_records", 0] }, 0, { $divide: [{ $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "-1"] }] }, 0] }, "$total_records"] }],
-                        },
-                    },
-                },
-            ]);
-            return result[0];
-        }
-        catch (error) {
-            console.error("Error fetching overall statistics:", error);
-            return null;
-        }
-    }
-    /**
-     * Gets message statistics grouped by activity.
-     */
-    async getStatisticsByActivity() {
-        try {
-            const results = await messageRecordModel_1.MessageRecord.aggregate([
-                // 1. Group by both activityId and status
-                {
-                    $group: {
-                        _id: { activityId: "$activityId", activityName: "$activityName", status: "$status" },
-                        count: { $sum: 1 },
-                    },
-                },
-                // 2. Regroup by activityId to summarize counts and get the activityName
-                {
-                    $group: {
-                        _id: "$_id.activityId", // Group by the ID
-                        activityName: { $first: "$_id.activityName" }, // Keep the name
-                        total_sent: { $sum: "$count" },
-                        sent_count: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
-                        delivered_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] },
-                        },
-                        opened_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
-                        },
-                        failed_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
-                        },
-                    },
-                },
-                // 3. Calculate rates and format the output
-                {
-                    $project: {
-                        _id: 0,
-                        activityId: "$_id",
-                        activityName: "$activityName",
-                        totalSent: "$total_sent",
-                        sent: "$sent_count",
-                        delivered: "$delivered_count",
-                        opened: "$opened_count",
-                        failed: "$failed_count",
-                        sentSuccessRate: {
-                            $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: [{ $sum: ["$sent_count", "$delivered_count", "$opened_count"] }, "$total_sent"] }],
-                        },
-                        tokenInvalidationRate: {
-                            $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: ["$failed_count", "$total_sent"] }],
-                        },
-                        deliveredRate: {
-                            $cond: [{ $eq: [{ $sum: ["$sent_count", "$delivered_count"] }, 0] }, 0, { $divide: ["$delivered_count", { $sum: ["$sent_count", "$delivered_count"] }] }],
-                        },
-                        openedRate: {
-                            $cond: [{ $eq: [{ $sum: ["$delivered_count", "$opened_count"] }, 0] }, 0, { $divide: ["$opened_count", { $sum: ["$delivered_count", "$opened_count"] }] }],
-                        },
-                    },
-                },
-                // 4. Sort by deliveredRate in descending order
-                {
-                    $sort: { deliveredRate: -1 },
-                },
-            ]);
-            return results;
-        }
-        catch (error) {
-            console.error("Error fetching statistics by activity:", error);
-            return [];
-        }
-    }
-    /**
-     * Gets message statistics grouped by strategy.
-     */
-    async getStatisticsByStrategy() {
-        try {
-            const results = await messageRecordModel_1.MessageRecord.aggregate([
-                // 1. Group by both strategyId and status
-                {
-                    $group: {
-                        _id: { strategyId: "$strategyId", strategyName: "$strategyName", status: "$status" },
-                        count: { $sum: 1 },
-                    },
-                },
-                // 2. Regroup by strategyId to summarize counts and get the strategyName
-                {
-                    $group: {
-                        _id: "$_id.strategyId",
-                        strategyName: { $first: "$_id.strategyName" },
-                        total_sent: { $sum: "$count" },
-                        sent_count: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
-                        delivered_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] },
-                        },
-                        opened_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
-                        },
-                        failed_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
-                        },
-                    },
-                },
-                // 3. Calculate rates and format the output
-                {
-                    $project: {
-                        _id: 0,
-                        strategyId: "$_id",
-                        strategyName: "$strategyName",
-                        totalSent: "$total_sent",
-                        sent: "$sent_count",
-                        delivered: "$delivered_count",
-                        opened: "$opened_count",
-                        failed: "$failed_count",
-                        sentSuccessRate: {
-                            $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: [{ $sum: ["$sent_count", "$delivered_count", "$opened_count"] }, "$total_sent"] }],
-                        },
-                        tokenInvalidationRate: {
-                            $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: ["$failed_count", "$total_sent"] }],
-                        },
-                        deliveredRate: {
-                            $cond: [{ $eq: [{ $sum: ["$sent_count", "$delivered_count"] }, 0] }, 0, { $divide: ["$delivered_count", { $sum: ["$sent_count", "$delivered_count"] }] }],
-                        },
-                        openedRate: {
-                            $cond: [{ $eq: [{ $sum: ["$delivered_count", "$opened_count"] }, 0] }, 0, { $divide: ["$opened_count", { $sum: ["$delivered_count", "$opened_count"] }] }],
-                        },
-                    },
-                },
-                // 4. Sort by deliveredRate in descending order
-                {
-                    $sort: { deliveredRate: -1 },
-                },
-            ]);
-            return results;
-        }
-        catch (error) {
-            console.error("Error fetching statistics by strategy:", error);
-            return [];
-        }
-    }
-    /**
-     * Gets message statistics grouped by template.
-     */
-    async getStatisticsByTemplate() {
-        try {
-            const results = await messageRecordModel_1.MessageRecord.aggregate([
-                // 1. Group by both templateId and status
-                {
-                    $group: {
-                        _id: { templateId: "$templateId", templateName: "$templateName", status: "$status" },
-                        count: { $sum: 1 },
-                    },
-                },
-                // 2. Regroup by templateId to summarize counts and get the templateName
-                {
-                    $group: {
-                        _id: "$_id.templateId",
-                        templateName: { $first: "$_id.templateName" },
-                        total_sent: { $sum: "$count" },
-                        sent_count: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
-                        delivered_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] },
-                        },
-                        opened_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
-                        },
-                        failed_count: {
-                            $sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
-                        },
-                    },
-                },
-                // 3. Calculate rates and format the output
-                {
-                    $project: {
-                        _id: 0,
-                        templateId: "$_id",
-                        templateName: "$templateName",
-                        totalSent: "$total_sent",
-                        sent: "$sent_count",
-                        delivered: "$delivered_count",
-                        opened: "$opened_count",
-                        failed: "$failed_count",
-                        sentSuccessRate: {
-                            $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: [{ $sum: ["$sent_count", "$delivered_count", "$opened_count"] }, "$total_sent"] }],
-                        },
-                        tokenInvalidationRate: {
-                            $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: ["$failed_count", "$total_sent"] }],
-                        },
-                        deliveredRate: {
-                            $cond: [{ $eq: [{ $sum: ["$sent_count", "$delivered_count"] }, 0] }, 0, { $divide: ["$delivered_count", { $sum: ["$sent_count", "$delivered_count"] }] }],
-                        },
-                        openedRate: {
-                            $cond: [{ $eq: [{ $sum: ["$delivered_count", "$opened_count"] }, 0] }, 0, { $divide: ["$opened_count", { $sum: ["$delivered_count", "$opened_count"] }] }],
-                        },
-                    },
-                },
-                // 4. Sort by deliveredRate in descending order
-                {
-                    $sort: { deliveredRate: -1 },
-                },
-            ]);
-            return results;
-        }
-        catch (error) {
-            console.error("Error fetching statistics by template:", error);
-            return [];
-        }
-    }
-    /**
-     * Gets daily message sending trends.
-     */
-    async getDailySentTrends() {
-        try {
-            const results = await messageRecordModel_1.MessageRecord.aggregate([
-                // 1. Truncate actualSendAt to the day, ignoring time
-                {
-                    $project: {
-                        _id: 0,
-                        date: {
-                            $dateTrunc: { date: "$actualSendAt", unit: "day", timezone: "America/Los_Angeles" },
-                        },
-                        status: "$status",
-                    },
-                },
-                // 2. Group by date and status
-                {
-                    $group: {
-                        _id: { date: "$date", status: "$status" },
-                        count: { $sum: 1 },
-                    },
-                },
-                // 3. Regroup to summarize counts by date
-                {
-                    $group: {
-                        _id: "$_id.date",
-                        totalSent: { $sum: "$count" },
-                        sent: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
-                        delivered: { $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] } },
-                        opened: { $sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] } },
-                        failed: { $sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] } },
-                    },
-                },
-                // 4. Calculate rates and format the output
-                {
-                    $project: {
-                        _id: 0,
-                        date: "$_id",
-                        totalSent: "$totalSent",
-                        sent: "$sent",
-                        delivered: "$delivered",
-                        opened: "$opened",
-                        failed: "$failed",
-                        // Sent success rate = (sent + delivered + opened) / total
-                        sentSuccessRate: {
-                            $cond: [{ $eq: ["$totalSent", 0] }, 0, { $divide: [{ $sum: ["$sent", "$delivered", "$opened"] }, "$totalSent"] }],
-                        },
-                        // Delivered rate = delivered / (sent + delivered)
-                        deliveredRate: {
-                            $cond: [{ $eq: [{ $sum: ["$sent", "$delivered"] }, 0] }, 0, { $divide: ["$delivered", { $sum: ["$sent", "$delivered"] }] }],
-                        },
-                        // Opened rate = opened / (delivered + opened)
-                        openedRate: {
-                            $cond: [{ $eq: [{ $sum: ["$delivered", "$opened"] }, 0] }, 0, { $divide: ["$opened", { $sum: ["$delivered", "$opened"] }] }],
-                        },
-                        // Token invalidation rate
-                        tokenInvalidationRate: {
-                            $cond: [{ $eq: ["$totalSent", 0] }, 0, { $divide: ["$failed", "$totalSent"] }],
-                        },
-                    },
-                },
-                // 5. Sort by date in ascending order
-                {
-                    $sort: { date: 1 },
-                },
-            ]);
-            return results;
-        }
-        catch (error) {
-            console.error("Error fetching daily sent trends:", error);
-            return [];
-        }
-    }
-    /**
-     * Calculates the average delivery time.
-     */
-    async getAverageDeliveryTime() {
-        try {
-            const result = await messageRecordModel_1.MessageRecord.aggregate([
-                // 1. Filter for delivered messages (status 2 or 3) with existing timestamps
-                {
-                    $match: {
-                        status: { $in: [2, 3] },
-                        actualSendAt: { $exists: true, $ne: null },
-                        deliveredAt: { $exists: true, $ne: null },
-                    },
-                },
-                // 2. Calculate the time difference (in milliseconds) from send to delivery
-                {
-                    $addFields: {
-                        time_to_deliver: {
-                            $subtract: ["$deliveredAt", "$actualSendAt"],
-                        },
-                    },
-                },
-                // 3. Calculate the average time
-                {
-                    $group: {
-                        _id: null,
-                        avg_time_to_deliver_ms: { $avg: "$time_to_deliver" },
-                    },
-                },
-                // 4. Convert to seconds for readability
-                {
-                    $project: {
-                        _id: 0,
-                        averageTimeToDeliverInSeconds: {
-                            $divide: ["$avg_time_to_deliver_ms", 1000],
-                        },
-                    },
-                },
-            ]);
-            return result[0];
-        }
-        catch (error) {
-            console.error("Error calculating average delivery time:", error);
-            return null;
-        }
-    }
-}
-exports.MessageRecordService = MessageRecordService;