import { Router } from "express"; import { z } from "zod"; import { prisma } from "@stamp/shared"; import { requireAuth } from "../middleware/auth.js"; const router = Router(); router.get("/rules", async (_req, res) => { const rules = await prisma.redemptionRule.findMany({ where: { enabled: true }, orderBy: { sortOrder: "asc" }, select: { id: true, name: true, description: true, threshold: true }, }); res.json({ success: true, data: rules }); }); const redeemSchema = z.object({ ruleId: z.string().uuid("规则 ID 格式不正确"), }); router.post("/redeem", requireAuth, async (req, res) => { const parsed = redeemSchema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ success: false, error: { code: "VALIDATION", message: parsed.error.issues[0].message } }); return; } const rule = await prisma.redemptionRule.findUnique({ where: { id: parsed.data.ruleId, enabled: true } }); if (!rule) { res.status(404).json({ success: false, error: { code: "NOT_FOUND", message: "兑换规则不存在" } }); return; } const collectionCount = await prisma.collection.count({ where: { userId: req.userId! } }); if (collectionCount < rule.threshold) { res.status(400).json({ success: false, error: { code: "INSUFFICIENT", message: `需要收集 ${rule.threshold} 枚图章,当前只有 ${collectionCount} 枚` }, }); return; } const redemption = await prisma.$transaction(async (tx) => { const record = await tx.redemption.create({ data: { userId: req.userId!, ruleId: rule.id, stampCount: collectionCount }, }); await tx.collection.deleteMany({ where: { userId: req.userId! } }); return record; }); res.json({ success: true, data: { id: redemption.id, ruleName: rule.name, stampCount: redemption.stampCount, redeemedAt: redemption.redeemedAt.toISOString(), }, }); }); router.get("/history", requireAuth, async (req, res) => { const records = await prisma.redemption.findMany({ where: { userId: req.userId! }, include: { rule: { select: { name: true } } }, orderBy: { redeemedAt: "desc" }, }); const data = records.map((r) => ({ id: r.id, ruleName: r.rule.name, stampCount: r.stampCount, redeemedAt: r.redeemedAt.toISOString(), })); res.json({ success: true, data }); }); export default router;