From de4f9a908d92a82e020cd5c159dab6576ca8bcfb Mon Sep 17 00:00:00 2001 From: TTigger Date: Sun, 12 Jul 2026 23:57:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(invoice):=20=E7=B5=B1=E4=B8=80=E7=99=BC?= =?UTF-8?q?=E7=A5=A8=E5=B0=8D=E7=8D=8E=EF=BC=88stage=20C=20item=201?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新工具「發票對獎」+全站第二支 serverless function。 - src/lib/invoice.ts:財政部 invoice.xml 解析與對獎規則(TDD,17 tests)。 漸進式對獎:末 3 碼即可確判「沒中」或六獎/增開六獎, 頭獎末碼系列逐碼升級,特別獎/特獎需 8 碼全中才確認。 號碼全程字串處理,前導零不掉。 - api/invoice-numbers.ts:代抓財政部公開 RSS(固定網址、免金鑰、 無使用者輸入故無 SSRF 面),CDN 快取 s-maxage=3600 + SWR 一天。 - src/tools/InvoiceLottery.tsx:期別切換、開獎號碼卡、即時對獎、 錯誤重試;中英雙語頁面,tools.ts 註冊(實用工具 🧧)。 - OG 卡:npm run og 產出 zh/en 兩張(68 張,og.test 守門),已目測。 - e2e:stub /api/invoice-numbers,涵蓋載入、快篩沒中、增開六獎、 8 碼頭獎、期別切換、API 掛掉重試(+3)。 測試:typecheck:api ✓ / 530 unit ✓(+17)/ 57 e2e ✓(+3) 合併後需立即驗 production /api/invoice-numbers(preview 在 SSO 後面)。 Co-Authored-By: Claude Fable 5 --- api/invoice-numbers.ts | 39 +++++ e2e/invoice.spec.ts | 79 +++++++++ public/og/en/invoice-lottery.png | Bin 0 -> 39462 bytes public/og/zh/invoice-lottery.png | Bin 0 -> 41579 bytes src/data/tools.ts | 1 + src/lib/__tests__/invoice.test.ts | 142 ++++++++++++++++ src/lib/invoice.ts | 115 +++++++++++++ src/pages/en/tools/invoice-lottery.astro | 12 ++ src/pages/tools/invoice-lottery.astro | 9 + src/tools/InvoiceLottery.tsx | 199 +++++++++++++++++++++++ 10 files changed, 596 insertions(+) create mode 100644 api/invoice-numbers.ts create mode 100644 e2e/invoice.spec.ts create mode 100644 public/og/en/invoice-lottery.png create mode 100644 public/og/zh/invoice-lottery.png create mode 100644 src/lib/__tests__/invoice.test.ts create mode 100644 src/lib/invoice.ts create mode 100644 src/pages/en/tools/invoice-lottery.astro create mode 100644 src/pages/tools/invoice-lottery.astro create mode 100644 src/tools/InvoiceLottery.tsx diff --git a/api/invoice-numbers.ts b/api/invoice-numbers.ts new file mode 100644 index 0000000..ae1aad4 --- /dev/null +++ b/api/invoice-numbers.ts @@ -0,0 +1,39 @@ +import type { VercelRequest, VercelResponse } from '@vercel/node'; +// 副檔名不可省:Vercel 以 nodenext 解析 api/,省略會編譯失敗(見 api/tsconfig.json) +import { parseInvoiceXml } from '../src/lib/invoice.js'; + +// 統一發票開獎號碼代理:抓財政部公開 RSS(固定網址、免金鑰), +// 解析成 JSON 回給前端。無使用者輸入,沒有 SSRF 面;不儲存任何資料。 +// 開獎兩個月一次,CDN 快取一小時綽綽有餘,也把流量壓到對得起官方伺服器。 + +const SOURCE = 'https://invoice.etax.nat.gov.tw/invoice.xml'; +const TIMEOUT_MS = 8000; +const UA = 'Mozilla/5.0 (compatible; TigletBot/1.0; +https://tiglet.vercel.app)'; + +export default async function handler(_req: VercelRequest, res: VercelResponse) { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), TIMEOUT_MS); + let xml: string; + try { + const response = await fetch(SOURCE, { signal: controller.signal, headers: { 'User-Agent': UA, Accept: 'application/xml,text/xml' } }); + if (!response.ok) { + res.setHeader('Cache-Control', 'no-store'); + return res.status(502).json({ error: 'fetch-failed', status: response.status }); + } + xml = await response.text(); + } catch { + res.setHeader('Cache-Control', 'no-store'); + return res.status(502).json({ error: 'fetch-failed' }); + } finally { + clearTimeout(timer); + } + + const periods = parseInvoiceXml(xml); + if (periods.length === 0) { + res.setHeader('Cache-Control', 'no-store'); + return res.status(502).json({ error: 'parse-failed' }); + } + + res.setHeader('Cache-Control', 'public, s-maxage=3600, stale-while-revalidate=86400'); + return res.status(200).json({ periods }); +} diff --git a/e2e/invoice.spec.ts b/e2e/invoice.spec.ts new file mode 100644 index 0000000..9b1b9fd --- /dev/null +++ b/e2e/invoice.spec.ts @@ -0,0 +1,79 @@ +import { test, expect } from '@playwright/test'; +import { waitForIslands } from './helpers'; + +const PERIODS = { + periods: [ + { + period: '115年 03~04月', + link: 'https://www.etax.nat.gov.tw/etw-main/ETW183W2_11503', + special: '19531471', + grand: '85941329', + first: ['07225810', '20231230', '83518781'], + sixth: ['985'], + }, + { + period: '115年 01~02月', + link: 'https://www.etax.nat.gov.tw/etw-main/ETW183W2_11501', + special: '87510041', + grand: '32220522', + first: ['21677046', '44662410', '31262513'], + sixth: [], + }, + ], +}; + +function stub(page: import('@playwright/test').Page) { + return page.route('**/api/invoice-numbers', (route) => + route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(PERIODS) }) + ); +} + +test('載入開獎號碼,末 3 碼沒中直接判定、8 碼對中頭獎', async ({ page }) => { + await stub(page); + await page.goto('/tools/invoice-lottery'); + await waitForIslands(page); + + // 最新一期的號碼上桌 + await expect(page.getByText('19531471')).toBeVisible(); + await expect(page.getByText('07225810')).toBeVisible(); + + const input = page.getByPlaceholder('例如 046'); + // 末 3 碼快篩:沒對中 → 明說「確定沒獎」 + await input.fill('000'); + await expect(page.getByRole('status')).toContainText('確定沒獎'); + + // 切到上一期,輸入完整 8 碼頭獎 + await page.getByLabel('開獎期別').selectOption({ label: '115年 01~02月' }); + await input.fill('21677046'); + await expect(page.getByRole('status')).toContainText('中頭獎'); + await expect(page.getByRole('status')).toContainText('200,000'); +}); + +test('末 3 碼對中增開六獎直接確定 200 元;對中頭獎末 3 提示可升級', async ({ page }) => { + await stub(page); + await page.goto('/tools/invoice-lottery'); + await waitForIslands(page); + + const input = page.getByPlaceholder('例如 046'); + await input.fill('985'); + await expect(page.getByRole('status')).toContainText('增開六獎'); + + await input.fill('810'); // 頭獎 07225810 的末 3 碼 + await expect(page.getByRole('status')).toContainText('六獎'); + await expect(page.getByRole('status')).toContainText('繼續輸入完整 8 碼'); +}); + +test('API 掛掉顯示錯誤並可重試', async ({ page }) => { + let calls = 0; + await page.route('**/api/invoice-numbers', (route) => { + calls += 1; + if (calls === 1) return route.fulfill({ status: 502, contentType: 'application/json', body: '{"error":"fetch-failed"}' }); + return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(PERIODS) }); + }); + await page.goto('/tools/invoice-lottery'); + await waitForIslands(page); + + await expect(page.getByText('開獎號碼取得失敗,請稍後再試。')).toBeVisible(); + await page.getByRole('button', { name: '重試' }).click(); + await expect(page.getByText('19531471')).toBeVisible(); +}); diff --git a/public/og/en/invoice-lottery.png b/public/og/en/invoice-lottery.png new file mode 100644 index 0000000000000000000000000000000000000000..7576358ff213c72d2e2e98626d6479c0f035cb53 GIT binary patch literal 39462 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 V?!h&LY&X*iceD*|RcSgr{$Fy6`HKJm literal 0 HcmV?d00001 diff --git a/public/og/zh/invoice-lottery.png b/public/og/zh/invoice-lottery.png new file mode 100644 index 0000000000000000000000000000000000000000..331aa002f88fef8d9ae8214648ab9d21ac8d8176 GIT binary patch literal 41579 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 YkL*ijY<%LGOM%Zh8;7&6tpgJN1)!?vnE(I) literal 0 HcmV?d00001 diff --git a/src/data/tools.ts b/src/data/tools.ts index 7ffa377..e489396 100644 --- a/src/data/tools.ts +++ b/src/data/tools.ts @@ -49,4 +49,5 @@ export const tools: Tool[] = [ { id: 'tw-id', title: '台灣證號', titleEn: 'Taiwan ID Validator', description: '身分證與統編檢查碼驗證,附測試號碼產生。', descriptionEn: 'Validate Taiwan national IDs and business numbers, with test generators.', category: '實用工具', path: '/tools/tw-id', icon: '🪪', status: 'available', keywords: ['身分證', '統編', '統一編號', 'id', 'gui', '驗證', 'validate', '檢查碼', 'taiwan'] }, { id: 'hash-id', title: '雜湊 / UUID', titleEn: 'Hash / UUID', description: '文字與檔案的 SHA 雜湊,加上 UUID 批次產生。', descriptionEn: 'SHA hashes for text and files, plus batch UUID generation.', category: '文字', path: '/tools/hash-id', icon: '🔏', status: 'available', keywords: ['hash', 'sha256', 'sha1', 'uuid', 'guid', '雜湊', 'checksum'] }, { id: 'url-audit', title: '網址健檢', titleEn: 'URL Audit', description: '檢查網址的 SEO、社群分享卡與 AI 引擎設定並評分。', descriptionEn: 'Audit a URL for SEO, social share cards and AI-engine readiness, with a score.', category: '實用工具', path: '/tools/url-audit', icon: '🔎', status: 'available', keywords: ['seo', 'og', 'open graph', 'geo', '健檢', 'meta', '分享', 'social', 'audit', 'twitter card'] }, + { id: 'invoice-lottery', title: '發票對獎', titleEn: 'Invoice Lottery', description: '財政部最新開獎號碼,輸入末 3 碼立刻對獎。', descriptionEn: 'Check Taiwan uniform-invoice numbers against the latest official draw.', category: '實用工具', path: '/tools/invoice-lottery', icon: '🧧', status: 'available', keywords: ['發票', '統一發票', '對獎', '中獎號碼', 'invoice', 'lottery', 'receipt', '雲端發票', '財政部'] }, ]; diff --git a/src/lib/__tests__/invoice.test.ts b/src/lib/__tests__/invoice.test.ts new file mode 100644 index 0000000..9743445 --- /dev/null +++ b/src/lib/__tests__/invoice.test.ts @@ -0,0 +1,142 @@ +import { describe, expect, it } from 'vitest'; +import { parseInvoiceXml, checkInvoice, PRIZE_AMOUNT, type WinningNumbers } from '../invoice'; + +// 財政部 invoice.xml 的實際格式(節錄自 https://invoice.etax.nat.gov.tw/invoice.xml) +const REAL_XML = ` + + + <![CDATA[統一發票中獎號碼]]> + + <![CDATA[115年 03~04月]]> + + 特別獎:19531471

特獎:85941329

頭獎:07225810、20231230、83518781

]]> + + + <![CDATA[115年 01~02月]]> + + 特別獎:87510041

特獎:32220522

頭獎:21677046、44662410、31262513

增開六獎:985、951

]]>
+
+ +`; + +describe('parseInvoiceXml', () => { + it('解析真實格式:期別、連結、各獎號碼', () => { + const periods = parseInvoiceXml(REAL_XML); + expect(periods).toHaveLength(2); + expect(periods[0]).toEqual({ + period: '115年 03~04月', + link: 'https://www.etax.nat.gov.tw/etw-main/ETW183W2_11503', + special: '19531471', + grand: '85941329', + first: ['07225810', '20231230', '83518781'], + sixth: [], + }); + // 增開六獎有開的期別要抓到;號碼是字串,前導零不能掉 + expect(periods[1].sixth).toEqual(['985', '951']); + expect(periods[1].grand).toBe('32220522'); + }); + + it('號碼不完整的 item 直接略過,不產生半套資料', () => { + const broken = REAL_XML.replace('

特獎:85941329

', ''); + const periods = parseInvoiceXml(broken); + expect(periods).toHaveLength(1); + expect(periods[0].period).toBe('115年 01~02月'); + }); + + it('垃圾輸入回空陣列', () => { + expect(parseInvoiceXml('not xml at all')).toEqual([]); + expect(parseInvoiceXml('')).toEqual([]); + }); +}); + +const W: WinningNumbers = { + period: '115年 01~02月', + link: '', + special: '87510041', + grand: '32220522', + first: ['21677046', '44662410', '31262513'], + sixth: ['985', '951'], +}; + +describe('checkInvoice(完整 8 碼)', () => { + it('特別獎:8 碼全中 1,000 萬', () => { + expect(checkInvoice('87510041', W)).toEqual({ level: 'special', amount: 10_000_000, matched: '87510041', confirmed: true, upgradable: false }); + }); + + it('特獎:8 碼全中 200 萬', () => { + expect(checkInvoice('32220522', W)).toMatchObject({ level: 'grand', amount: 2_000_000 }); + }); + + it('頭獎全中 20 萬;末 7~3 碼對應二獎到六獎', () => { + expect(checkInvoice('21677046', W)).toMatchObject({ level: 'first', amount: 200_000 }); + expect(checkInvoice('91677046', W)).toMatchObject({ level: 'second', amount: 40_000 }); + expect(checkInvoice('99677046', W)).toMatchObject({ level: 'third', amount: 10_000 }); + expect(checkInvoice('99977046', W)).toMatchObject({ level: 'fourth', amount: 4_000 }); + expect(checkInvoice('99997046', W)).toMatchObject({ level: 'fifth', amount: 1_000 }); + expect(checkInvoice('99999046', W)).toMatchObject({ level: 'sixth', amount: 200 }); + }); + + it('增開六獎:末 3 碼相同 200 元', () => { + expect(checkInvoice('12345985', W)).toMatchObject({ level: 'addSixth', amount: 200 }); + }); + + it('特別獎末碼近似但非全中不算獎(特別獎只比全 8 碼)', () => { + // 末 7 碼與特別獎相同、與頭獎無關 → 沒中 + expect(checkInvoice('97510041', W)).toBeNull(); + }); + + it('多重命中取最高獎金', () => { + // 同時是頭獎末4(五獎)與增開六獎? 構造:末3=985 且末4 對中頭獎末4 —— 取五獎 1000 + const w2: WinningNumbers = { ...W, first: ['11112985'], sixth: ['985'] }; + expect(checkInvoice('99992985', w2)).toMatchObject({ level: 'fifth', amount: 1_000 }); + }); + + it('沒中回 null', () => { + expect(checkInvoice('00000000', W)).toBeNull(); + }); +}); + +describe('checkInvoice(末 3~7 碼快速對獎)', () => { + it('末 3 碼對中頭獎末 3 → 至少六獎已確定,且可能升級', () => { + const hit = checkInvoice('046', W)!; + expect(hit).toMatchObject({ level: 'sixth', amount: 200, confirmed: true }); + expect(hit.upgradable).toBe(true); // 補完 8 碼可能是二獎以上 + }); + + it('末 3 碼對中增開六獎 → 200 元確定,不會再升級', () => { + const hit = checkInvoice('951', W)!; + expect(hit).toMatchObject({ level: 'addSixth', amount: 200, confirmed: true }); + expect(hit.upgradable).toBe(false); + }); + + it('末 3 碼只對中特別獎末 3 → 未確定(特別獎要全 8 碼),confirmed false', () => { + const hit = checkInvoice('041', W)!; + expect(hit.confirmed).toBe(false); + expect(hit.level).toBe('special'); + }); + + it('末 3 碼什麼都沒對中 → null(可以確定沒中,不用再輸入)', () => { + expect(checkInvoice('000', W)).toBeNull(); + }); + + it('末 5 碼對中頭獎末 5 → 四獎確定、可能再升級', () => { + expect(checkInvoice('77046', W)).toMatchObject({ level: 'fourth', amount: 4_000, confirmed: true, upgradable: true }); + expect(checkInvoice('62513', W)).toMatchObject({ level: 'fourth', amount: 4_000, confirmed: true }); + }); + + it('少於 3 碼或含非數字 → null(無效輸入)', () => { + expect(checkInvoice('04', W)).toBeNull(); + expect(checkInvoice('abc46', W)).toBeNull(); + expect(checkInvoice('', W)).toBeNull(); + }); +}); + +describe('PRIZE_AMOUNT', () => { + it('獎金表符合財政部公告', () => { + expect(PRIZE_AMOUNT.special).toBe(10_000_000); + expect(PRIZE_AMOUNT.grand).toBe(2_000_000); + expect(PRIZE_AMOUNT.first).toBe(200_000); + expect(PRIZE_AMOUNT.sixth).toBe(200); + expect(PRIZE_AMOUNT.addSixth).toBe(200); + }); +}); diff --git a/src/lib/invoice.ts b/src/lib/invoice.ts new file mode 100644 index 0000000..04676e6 --- /dev/null +++ b/src/lib/invoice.ts @@ -0,0 +1,115 @@ +// 統一發票對獎:財政部 invoice.xml 解析與對獎規則。純函式, +// 由 api/invoice-numbers.ts(解析)與 InvoiceLottery 島(對獎)共用。 +// 號碼一律以字串處理,前導零不能掉(例:特獎 00507588)。 + +export interface WinningNumbers { + period: string; // 期別,如「115年 03~04月」 + link: string; // 財政部該期公告頁 + special: string; // 特別獎(8 碼,全中 1,000 萬) + grand: string; // 特獎(8 碼,全中 200 萬) + first: string[]; // 頭獎(8 碼三組;末 7~3 碼對應二獎~六獎) + sixth: string[]; // 增開六獎(3 碼,可能沒開) +} + +export type PrizeLevel = 'special' | 'grand' | 'first' | 'second' | 'third' | 'fourth' | 'fifth' | 'sixth' | 'addSixth'; + +export const PRIZE_AMOUNT: Record = { + special: 10_000_000, + grand: 2_000_000, + first: 200_000, + second: 40_000, + third: 10_000, + fourth: 4_000, + fifth: 1_000, + sixth: 200, + addSixth: 200, +}; + +/** 與頭獎共同末碼長度 → 獎級(8 碼全中即頭獎) */ +const SUFFIX_LEVEL: Record = { + 8: 'first', + 7: 'second', + 6: 'third', + 5: 'fourth', + 4: 'fifth', + 3: 'sixth', +}; + +export interface PrizeHit { + level: PrizeLevel; + amount: number; + matched: string; // 對中的官方號碼 + confirmed: boolean; // 以目前輸入位數已確定中此獎;false = 需要完整 8 碼才能確認(特別獎/特獎) + upgradable: boolean; // 再多輸入幾碼有機會升到更高獎級 +} + +export function parseInvoiceXml(xml: string): WinningNumbers[] { + const items = xml.match(/[\s\S]*?<\/item>/g) ?? []; + const out: WinningNumbers[] = []; + for (const item of items) { + const cdata = (tag: string) => item.match(new RegExp(`<${tag}>`))?.[1]?.trim() ?? ''; + const desc = cdata('description'); + const period = cdata('title'); + const special = desc.match(/特別獎:(\d{8})/)?.[1]; + const grand = desc.match(/特獎:(\d{8})/)?.[1]; + const first = desc.match(/頭獎:([\d、,\s]+)/)?.[1]?.match(/\d{8}/g) ?? []; + const sixth = desc.match(/增開六獎:([\d、,\s]+)/)?.[1]?.match(/\d{3}/g) ?? []; + // 缺任何必要獎項就整期略過,寧缺勿錯 + if (!period || !special || !grand || first.length === 0) continue; + out.push({ period, link: cdata('link'), special, grand, first, sixth }); + } + return out; +} + +function commonSuffixLen(a: string, b: string): number { + let n = 0; + while (n < a.length && n < b.length && a[a.length - 1 - n] === b[b.length - 1 - n]) n++; + return n; +} + +/** + * 對獎。輸入發票號碼的「末 3~8 碼」: + * - 8 碼:完整判定(特別獎/特獎要全中;頭獎系列取最長共同末碼;增開六獎比末 3)。 + * - 3~7 碼:能確定的獎(頭獎末碼系列、增開六獎)直接判定; + * 只有特別獎/特獎的末碼吻合時回未確認命中(confirmed: false),提示補完 8 碼。 + * - 什麼都沒對中回 null —— 末 3 碼就沒中的發票可以直接確定沒獎。 + */ +export function checkInvoice(digits: string, w: WinningNumbers): PrizeHit | null { + if (!/^\d{3,8}$/.test(digits)) return null; + const k = digits.length; + const full = k === 8; + + const confirmed: PrizeHit[] = []; + + if (full && digits === w.special) confirmed.push({ level: 'special', amount: PRIZE_AMOUNT.special, matched: w.special, confirmed: true, upgradable: false }); + if (full && digits === w.grand) confirmed.push({ level: 'grand', amount: PRIZE_AMOUNT.grand, matched: w.grand, confirmed: true, upgradable: false }); + + // 頭獎系列:共同末碼長度不能超過已輸入的位數;部分輸入時整段吻合(L === k)才有升級空間 + let anyFullTailMatch = false; + for (const f of w.first) { + const L = Math.min(commonSuffixLen(digits, f), k); + if (L < 3) continue; + if (!full && L === k) anyFullTailMatch = true; + confirmed.push({ level: SUFFIX_LEVEL[L], amount: PRIZE_AMOUNT[SUFFIX_LEVEL[L]], matched: f, confirmed: true, upgradable: false }); + } + + const last3 = digits.slice(-3); + if (w.sixth.includes(last3)) confirmed.push({ level: 'addSixth', amount: PRIZE_AMOUNT.addSixth, matched: last3, confirmed: true, upgradable: false }); + + // 特別獎/特獎:未滿 8 碼只能算「有機會」,要補完才能確認 + const potential: PrizeHit[] = []; + if (!full) { + if (w.special.endsWith(digits)) potential.push({ level: 'special', amount: PRIZE_AMOUNT.special, matched: w.special, confirmed: false, upgradable: false }); + if (w.grand.endsWith(digits)) potential.push({ level: 'grand', amount: PRIZE_AMOUNT.grand, matched: w.grand, confirmed: false, upgradable: false }); + } + + const best = (hits: PrizeHit[]) => hits.reduce((a, b) => (b.amount > a.amount ? b : a)); + + if (confirmed.length > 0) { + const hit = best(confirmed); + hit.upgradable = !full && (anyFullTailMatch || potential.length > 0); + return hit; + } + if (potential.length > 0) return best(potential); + return null; +} diff --git a/src/pages/en/tools/invoice-lottery.astro b/src/pages/en/tools/invoice-lottery.astro new file mode 100644 index 0000000..67b0a31 --- /dev/null +++ b/src/pages/en/tools/invoice-lottery.astro @@ -0,0 +1,12 @@ +--- +import BaseLayout from '../../../layouts/BaseLayout.astro'; +import InvoiceLottery from '../../../tools/InvoiceLottery.tsx'; +import { tools } from '../../../data/tools'; + +const tool = tools.find((t) => t.id === 'invoice-lottery')!; +--- + + ← All tools +

{tool.titleEn}

+ +
diff --git a/src/pages/tools/invoice-lottery.astro b/src/pages/tools/invoice-lottery.astro new file mode 100644 index 0000000..0cc3b4e --- /dev/null +++ b/src/pages/tools/invoice-lottery.astro @@ -0,0 +1,9 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import InvoiceLottery from '../../tools/InvoiceLottery.tsx'; +--- + + ← 回到所有工具 +

發票對獎

+ +
diff --git a/src/tools/InvoiceLottery.tsx b/src/tools/InvoiceLottery.tsx new file mode 100644 index 0000000..eadd072 --- /dev/null +++ b/src/tools/InvoiceLottery.tsx @@ -0,0 +1,199 @@ +import { useEffect, useState } from 'react'; +import { checkInvoice, type PrizeLevel, type WinningNumbers } from '../lib/invoice'; +import type { Locale } from '../lib/i18n'; + +const L = { + zh: { + loading: '正在取得財政部開獎號碼…', + loadError: '開獎號碼取得失敗,請稍後再試。', + retry: '重試', + periodLabel: '開獎期別', + special: '特別獎', + grand: '特獎', + first: '頭獎', + addSixth: '增開六獎', + specialNote: '8 碼全中 1,000 萬', + grandNote: '8 碼全中 200 萬', + firstNote: '全中 20 萬;末 7~3 碼依序 4 萬/1 萬/4 千/1 千/200', + addSixthNote: '末 3 碼相同 200 元', + inputLabel: '輸入發票末 3 碼(可續輸到 8 碼確認獎級)', + inputPlaceholder: '例如 046', + tooShort: '輸入末 3 碼即可開始對獎', + noneShort: (n: number) => `末 ${n} 碼沒有對中 —— 這張確定沒獎,不用再輸入了`, + noneFull: '完整 8 碼沒有對中,這張沒獎', + win: (name: string, amount: string) => `🎉 中${name}!獎金 ${amount} 元`, + winMatched: (num: string) => `對中號碼:${num}`, + upgradeHint: '繼續輸入完整 8 碼,有機會是更高獎級!', + maybe: (name: string) => `末碼與${name}相同!輸入完整 8 碼確認是否全中`, + officialLink: '財政部官方公告', + levelNames: { + special: '特別獎', grand: '特獎', first: '頭獎', second: '二獎', third: '三獎', + fourth: '四獎', fifth: '五獎', sixth: '六獎', addSixth: '增開六獎', + } as Record, + rules: '二獎~六獎為發票末碼與任一組頭獎末碼相同;特別獎與特獎必須 8 碼全中,末碼相同不算。中獎請以財政部公告為準,並留意領獎期限。', + privacy: '對獎完全在你的瀏覽器進行;伺服器只代抓財政部公告的開獎號碼,你輸入的發票號碼不會送出。', + }, + en: { + loading: 'Fetching winning numbers from the Ministry of Finance…', + loadError: 'Could not load the winning numbers. Please try again.', + retry: 'Retry', + periodLabel: 'Draw period', + special: 'Special Prize', + grand: 'Grand Prize', + first: 'First Prize', + addSixth: 'Additional Sixth', + specialNote: 'all 8 digits — NT$10,000,000', + grandNote: 'all 8 digits — NT$2,000,000', + firstNote: 'all 8 — NT$200,000; last 7–3 digits win 40k / 10k / 4k / 1k / 200', + addSixthNote: 'last 3 digits — NT$200', + inputLabel: 'Enter the last 3 digits (keep typing up to 8 to confirm the tier)', + inputPlaceholder: 'e.g. 046', + tooShort: 'Type the last 3 digits to start checking', + noneShort: (n: number) => `Last ${n} digits don't match — this one is definitely not a winner`, + noneFull: 'No match on the full 8 digits — not a winner', + win: (name: string, amount: string) => `🎉 ${name} — NT$${amount}!`, + winMatched: (num: string) => `Matched number: ${num}`, + upgradeHint: 'Keep typing the full 8 digits — it could be a higher tier!', + maybe: (name: string) => `The tail matches the ${name}! Enter all 8 digits to confirm`, + officialLink: 'Official announcement', + levelNames: { + special: 'Special Prize', grand: 'Grand Prize', first: 'First Prize', second: 'Second Prize', + third: 'Third Prize', fourth: 'Fourth Prize', fifth: 'Fifth Prize', sixth: 'Sixth Prize', + addSixth: 'Additional Sixth Prize', + } as Record, + rules: 'Second to Sixth Prizes require the invoice tail to match a First Prize number; the Special and Grand Prizes require all 8 digits. Results are subject to the official announcement — mind the claim deadline.', + privacy: 'Checking happens entirely in your browser; the server only relays the official winning numbers. Your invoice number is never sent anywhere.', + }, +} as const; +type Dict = (typeof L)[Locale]; + +const numClass = 'font-mono tabular-nums text-ink'; + +function WinningCard({ w, t }: { w: WinningNumbers; t: Dict }) { + const rows: { label: string; note: string; nums: string[] }[] = [ + { label: t.special, note: t.specialNote, nums: [w.special] }, + { label: t.grand, note: t.grandNote, nums: [w.grand] }, + { label: t.first, note: t.firstNote, nums: w.first }, + ...(w.sixth.length > 0 ? [{ label: t.addSixth, note: t.addSixthNote, nums: w.sixth }] : []), + ]; + return ( +
+
+ {rows.map((r) => ( +
+
{r.label}
+
+ {r.nums.map((n) => ( + {n} + ))} +
+
{r.note}
+
+ ))} +
+ {w.link && ( + + {t.officialLink} ↗ + + )} +
+ ); +} + +function Result({ digits, w, t }: { digits: string; w: WinningNumbers; t: Dict }) { + if (digits.length < 3) return

{t.tooShort}

; + const hit = checkInvoice(digits, w); + if (!hit) { + return ( +

+ {digits.length < 8 ? t.noneShort(digits.length) : t.noneFull} +

+ ); + } + const name = t.levelNames[hit.level]; + if (!hit.confirmed) { + return

{t.maybe(name)}

; + } + return ( +
+

{t.win(name, hit.amount.toLocaleString())}

+

{t.winMatched(hit.matched)}

+ {hit.upgradable &&

{t.upgradeHint}

} +
+ ); +} + +export default function InvoiceLottery({ locale = 'zh' }: { locale?: Locale }) { + const t = L[locale]; + const [periods, setPeriods] = useState(null); + const [failed, setFailed] = useState(false); + const [periodIdx, setPeriodIdx] = useState(0); + const [digits, setDigits] = useState(''); + + async function load() { + setFailed(false); + setPeriods(null); + try { + const res = await fetch('/api/invoice-numbers'); + if (!res.ok) throw new Error(); + const data = await res.json(); + if (!Array.isArray(data.periods) || data.periods.length === 0) throw new Error(); + setPeriods(data.periods); + } catch { + setFailed(true); + } + } + useEffect(() => { + load(); + }, []); + + if (failed) { + return ( +
+

{t.loadError}

+ +
+ ); + } + if (!periods) return

{t.loading}

; + + const w = periods[Math.min(periodIdx, periods.length - 1)]; + + return ( +
+ + + + + + + + +

{t.rules}

+

{t.privacy}

+
+ ); +}