80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
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;
|