backfill_2months.mjs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // 补齐近两个月:有学习记录(Flag=0)但缺 MiaoguoTask 记录的练习日,并对受影响用户完整重算 DayNumber/里程碑/勤勉号。
  2. import moment from 'moment';
  3. import Miaoguo from '../src/model/miaoguo.js';
  4. import constantClass from '../src/util/constant/index.js';
  5. import { stringUtils } from '../src/util/stringClass.js';
  6. const startDate = new Date(Date.now() - 60 * 86400000).toISOString().slice(0, 10);
  7. const dayKey = (x) => moment(x.CreateTime).format('YYYY-MM-DD');
  8. const score = (x) => (x.IsShare > 0 ? 4 : 0) + (x.MilestoneDayNumber > 0 ? 2 : 0) + (x.AphorismID > 0 ? 1 : 0);
  9. // 1) 找出缺失的练习日
  10. const missing = await Miaoguo.RunSql([startDate], `
  11. SELECT mc.UserID, DATE_FORMAT(r.CreateTime,'%Y-%m-%d') d,
  12. COUNT(DISTINCT r.CardID) CardCount, SUM(r.Duration) Duration
  13. FROM MiaoguoLearningRecord r
  14. INNER JOIN MiaoguoCard mc ON mc.MiaoguoCardID = r.CardID
  15. LEFT JOIN MiaoguoTask t ON t.UserID = mc.UserID AND DATE(t.CreateTime) = DATE(r.CreateTime)
  16. WHERE r.Flag=0 AND r.CreateTime >= ? AND t.ID IS NULL
  17. GROUP BY mc.UserID, DATE_FORMAT(r.CreateTime,'%Y-%m-%d')
  18. ORDER BY mc.UserID, d;`);
  19. console.log('待补齐的练习日:', missing.length, '涉及用户:', [...new Set(missing.map(r => r.UserID))].length);
  20. if (missing.length === 0) process.exit(0);
  21. const byUser = new Map();
  22. for (const r of missing) {
  23. if (!byUser.has(r.UserID)) byUser.set(r.UserID, []);
  24. byUser.get(r.UserID).push(r);
  25. }
  26. // 2) 插入缺失行(带存在性保护)
  27. let inserted = 0;
  28. for (const [userID, days] of byUser) {
  29. for (const d of days) {
  30. const createTime = d.d + ' 00:00:00';
  31. await Miaoguo.RunSql([userID, createTime, d.CardCount, d.Duration, userID, createTime],
  32. "insert into MiaoguoTask (UserID,CreateTime,CardTask,Duration,IsShare,Flag) " +
  33. "select ?,?,?,?,0,0 from dual where not exists " +
  34. "(select UserID,CreateTime from MiaoguoTask where UserID=? and CreateTime=?) limit 1;");
  35. inserted++;
  36. }
  37. console.log(`U${userID}: 补插 ${days.length} 天 ->`, days.map(x => x.d).join(','));
  38. }
  39. console.log('补插完成,共', inserted, '天。\n');
  40. // 3) 对受影响用户完整重算(与夜间重算一致)
  41. const users = [...byUser.keys()].map(Number);
  42. const placeholders = users.map(() => '?').join(',');
  43. await Miaoguo.RunSql(users, `update MiaoguoTask set MilestoneID=0,MilestoneDayNumber=0,HardworkingNumber=0 where UserID in (${placeholders});`);
  44. const milestone = constantClass.GetMilestone();
  45. for (const userID of users) {
  46. const tasks = await Miaoguo.RunSql([userID],
  47. "SELECT * FROM MiaoguoTask where CardTask>0 and UserID=? order by CreateTime, ID desc;");
  48. if (!tasks || tasks.length === 0) continue;
  49. const dayBest = new Map();
  50. for (const t of tasks) {
  51. const key = dayKey(t);
  52. const best = dayBest.get(key);
  53. if (!best) dayBest.set(key, t);
  54. else if (score(t) > score(best) || (score(t) === score(best) && t.ID > best.ID))
  55. dayBest.set(key, t);
  56. }
  57. for (const t of tasks) {
  58. if (dayBest.get(dayKey(t)).ID !== t.ID)
  59. await Miaoguo.RunSql([t.ID], "delete from MiaoguoTask where ID=?;");
  60. }
  61. const dayTasks = [...dayBest.values()].sort((a, b) =>
  62. a.CreateTime < b.CreateTime ? -1 : a.CreateTime > b.CreateTime ? 1 : a.ID - b.ID);
  63. let milestoneIDs = [];
  64. for (let i = 0; i < 40; i++) milestoneIDs.push(i + 1);
  65. milestoneIDs = stringUtils.RandomArray(milestoneIDs);
  66. let milestoneIndex = 0, hardworkingIndex = 0;
  67. for (let i = 0; i < dayTasks.length; i++) {
  68. const t = dayTasks[i];
  69. const update = { ID: t.ID, DayNumber: i + 1 };
  70. const milestoneItem = milestone.find(item => item.DayNumber == i + 1);
  71. if (milestoneItem) {
  72. update.MilestoneDayNumber = milestoneItem.DayNumber;
  73. update.MilestoneID = milestoneIDs[milestoneIndex++];
  74. }
  75. if (t.IsShare > 0 || update.MilestoneDayNumber > 0 || t.AphorismID > 0)
  76. update.HardworkingNumber = ++hardworkingIndex;
  77. await Miaoguo.UpdateMiaoguoTask(update);
  78. }
  79. console.log(`U${userID}: 重算完成,共 ${dayTasks.length} 天 (1..${dayTasks.length})`);
  80. }
  81. console.log('\n全部补齐完成。');
  82. process.exit(0);