recalculate-total-done-rate.js 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. const doneRateModel_1 = __importDefault(require("../../src/models/doneRateModel"));
  6. const totalDoneRateModel_1 = __importDefault(require("../../src/models/totalDoneRateModel"));
  7. /**
  8. * 重新计算 TotalDoneRate 表中的所有累计字段。
  9. * 通过聚合 DoneRate 表中的所有历史数据来实现。
  10. * 此脚本应在 DoneRate 表的 tipCount 字段回填完成后运行。
  11. * @returns Promise<string> - 返回累计更新结果的摘要信息。
  12. */
  13. async function runRecalculation() {
  14. console.log("[TotalDoneRate Recalculation] Starting full recalculation of TotalDoneRate table...");
  15. const startTime = Date.now();
  16. try {
  17. // 1. 使用聚合操作,从 DoneRate 表中计算每个作品的总累计数据
  18. console.log("[TotalDoneRate Recalculation] Step 1: Aggregating all DoneRate records...");
  19. // 聚合管道定义
  20. const aggregationPipeline = [
  21. {
  22. // 1. 按作品 ID (res) 分组
  23. $group: {
  24. _id: "$res", // 使用 res 作为分组键
  25. totalStartCount: { $sum: "$startCount" },
  26. totalDoneCount: { $sum: "$doneCount" },
  27. totalTipCount: { $sum: "$tipCount" }, // 累加 tipCount
  28. },
  29. },
  30. {
  31. // 2. 计算最终的 completionRate
  32. $project: {
  33. _id: "$_id",
  34. totalStartCount: "$totalStartCount",
  35. totalDoneCount: "$totalDoneCount",
  36. totalTipCount: "$totalTipCount",
  37. completionRate: {
  38. $cond: [
  39. { $gt: ["$totalStartCount", 0] },
  40. { $multiply: [{ $divide: ["$totalDoneCount", "$totalStartCount"] }, 100] },
  41. 0, // 如果 totalStartCount 为 0,则 completionRate 为 0
  42. ],
  43. },
  44. },
  45. },
  46. ];
  47. const aggregatedResults = await doneRateModel_1.default.aggregate(aggregationPipeline).exec();
  48. console.log(`[TotalDoneRate Recalculation] Aggregation complete. Found ${aggregatedResults.length} unique artworks to update.`);
  49. // 2. 批量更新 TotalDoneRate 表
  50. const bulkOps = aggregatedResults.map((result) => ({
  51. updateOne: {
  52. // 查找条件:使用聚合结果的 _id (即作品 ObjectId)
  53. filter: { _id: result._id },
  54. // 更新操作:设置所有累计字段
  55. update: {
  56. $set: {
  57. totalStartCount: result.totalStartCount,
  58. totalDoneCount: result.totalDoneCount,
  59. totalTipCount: result.totalTipCount, // 设置重新计算的总道具使用数
  60. completionRate: result.completionRate,
  61. },
  62. },
  63. // 如果 TotalDoneRate 记录不存在,则创建新记录
  64. upsert: true,
  65. },
  66. }));
  67. if (bulkOps.length > 0) {
  68. console.log(`[TotalDoneRate Recalculation] Step 2: Running bulk update for ${bulkOps.length} documents...`);
  69. const updateResult = await totalDoneRateModel_1.default.bulkWrite(bulkOps, { ordered: false });
  70. const modifiedCount = (updateResult.modifiedCount || 0) + (updateResult.upsertedCount || 0);
  71. const endTime = Date.now();
  72. const timeTaken = ((endTime - startTime) / 1000).toFixed(2);
  73. const summary = `[TotalDoneRate Recalculation] Full recalculation completed in ${timeTaken} seconds. Total artworks updated/created: ${modifiedCount}.`;
  74. console.log(`\n${summary}`);
  75. return summary;
  76. }
  77. else {
  78. const summary = "[TotalDoneRate Recalculation] Aggregation returned no results. TotalDoneRate table update skipped.";
  79. console.log(summary);
  80. return summary;
  81. }
  82. }
  83. catch (error) {
  84. console.error("[TotalDoneRate Recalculation] Fatal error during full recalculation:", error);
  85. throw new Error("Failed to recalculate TotalDoneRate history.");
  86. }
  87. }
  88. module.exports = { runRecalculation };