nuno 已修改 1 month ago. 還原成這個修訂版本
1 file changed, 111 insertions
tg.ts(檔案已創建)
| @@ -0,0 +1,111 @@ | |||
| 1 | + | /** | |
| 2 | + | * TG — thin dispatcher for TG-related operations. | |
| 3 | + | * Orchestrates Score, Attendance, WRank, and Bringer. | |
| 4 | + | * | |
| 5 | + | * Usage: | |
| 6 | + | * import { TG } from "@systems/tg"; | |
| 7 | + | * | |
| 8 | + | * TG.currentWeek() | |
| 9 | + | * TG.resetWeek() | |
| 10 | + | * TG.getAttendance({ historyKey }) | |
| 11 | + | * TG.getScore({ character, slot }) | |
| 12 | + | * TG.getWeeklySummary({ character }) | |
| 13 | + | */ | |
| 14 | + | ||
| 15 | + | import { Nation, Character, UserKey, SlotHour, TGScore } from "@types"; | |
| 16 | + | import { WRank } from "@systems/wrank"; | |
| 17 | + | import { Bringer } from "@systems/bringer"; | |
| 18 | + | import { Score, WeeklySummary } from "@systems/score"; | |
| 19 | + | import { Attendance } from "@systems/attendance"; | |
| 20 | + | import { Config } from "@systems/config"; | |
| 21 | + | import { TGKey } from "@systems/tg-key"; | |
| 22 | + | ||
| 23 | + | export const TG = { | |
| 24 | + | // ── Week ────────────────────────────────────────────────────────────────── | |
| 25 | + | ||
| 26 | + | currentWeek() { | |
| 27 | + | return WRank.currentWeek(); | |
| 28 | + | }, | |
| 29 | + | ||
| 30 | + | /** | |
| 31 | + | * Reset to a new week — carry Bringer forward, clear ranks. | |
| 32 | + | * Called every Monday 00:00 by the Scheduler. | |
| 33 | + | */ | |
| 34 | + | resetWeek(): void { | |
| 35 | + | const now = new Date(); | |
| 36 | + | const newWeekKey = WRank.weekKey(now); | |
| 37 | + | const prevWeekKey = WRank.weekKey(new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)); | |
| 38 | + | ||
| 39 | + | // Access internal data via WRank | |
| 40 | + | const prevWeek = WRank.weekFromKey(prevWeekKey); | |
| 41 | + | const newWeek = WRank.currentWeek(); // ensures new week exists | |
| 42 | + | ||
| 43 | + | if (prevWeek) { | |
| 44 | + | const goal = Config.get({ section: "wrank", key: "goal" }); | |
| 45 | + | for (const nation of [Nation.Capella, Nation.Procyon]) { | |
| 46 | + | const entries = prevWeek.entries[nation]; | |
| 47 | + | const rank1 = entries.find((e) => e.currentRank === 1); | |
| 48 | + | ||
| 49 | + | // Rank 1 with >= goal TGs becomes Bringer — no exceptions | |
| 50 | + | // Officers use Bringer.override() for manual adjustments | |
| 51 | + | if (rank1 && rank1.tgCount >= goal) { | |
| 52 | + | newWeek.bringer[nation] = rank1.characterName; | |
| 53 | + | } else { | |
| 54 | + | newWeek.bringer[nation] = null; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | // Overrides do NOT carry forward — each week starts clean | |
| 58 | + | delete (newWeek.bringer as any)[`${nation}Override`]; | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | WRank.save(); | |
| 63 | + | console.log(`[TG] Week reset to ${newWeekKey}. Bringer:`, newWeek.bringer); | |
| 64 | + | }, | |
| 65 | + | ||
| 66 | + | // ── Attendance ──────────────────────────────────────────────────────────── | |
| 67 | + | ||
| 68 | + | getAttendance({ historyKey, nation }: { | |
| 69 | + | historyKey: TGKey; | |
| 70 | + | nation?: Nation; | |
| 71 | + | }): UserKey[] { | |
| 72 | + | const players = Attendance.players(historyKey); | |
| 73 | + | if (!nation) return players; | |
| 74 | + | // Filter by nation requires character lookup — caller handles if needed | |
| 75 | + | return players; | |
| 76 | + | }, | |
| 77 | + | ||
| 78 | + | allSubmitted(historyKey: TGKey): boolean { | |
| 79 | + | return Attendance.allSubmitted(historyKey); | |
| 80 | + | }, | |
| 81 | + | ||
| 82 | + | // ── Score ───────────────────────────────────────────────────────────────── | |
| 83 | + | ||
| 84 | + | getScore({ character, slot, date }: { | |
| 85 | + | character: Character; | |
| 86 | + | slot: SlotHour; | |
| 87 | + | date?: string; | |
| 88 | + | }): TGScore | null { | |
| 89 | + | return Score.get({ character, slot }); | |
| 90 | + | }, | |
| 91 | + | ||
| 92 | + | getWeeklySummary({ character }: { character: Character }): WeeklySummary { | |
| 93 | + | return Score.getWeeklySummary({ character }); | |
| 94 | + | }, | |
| 95 | + | ||
| 96 | + | submitScore(params: Parameters<typeof Score.submit>[0]): void { | |
| 97 | + | Score.submit(params); | |
| 98 | + | }, | |
| 99 | + | ||
| 100 | + | // ── Bringer ─────────────────────────────────────────────────────────────── | |
| 101 | + | ||
| 102 | + | getBringer({ nation }: { nation: Nation }): string | null { | |
| 103 | + | return Bringer.get({ nation }); | |
| 104 | + | }, | |
| 105 | + | ||
| 106 | + | // ── W.Rank ──────────────────────────────────────────────────────────────── | |
| 107 | + | ||
| 108 | + | getEntry(character: Character) { | |
| 109 | + | return WRank.entry(character.name, character.nation); | |
| 110 | + | }, | |
| 111 | + | }; | |
上一頁
下一頁