UI-UX/scripts/inspect-db.mjs
iye 10878ddb3f
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
feat(vote): 重构投票模型为终身 12 票 + 每艺人 1 票
前端:
- store 改为 votedArtists[] + zustand persist
- VoteModal 删除 1/3/5/ALL 选择器,改三态(待投/已投/满额)
- 卡片/排行/详情页加 hasVoted 状态 + ✓ 角标
- Hero 右上角 Countdown 替换为 HeroVoteProgress(12 格点亮进度)
- /me 改为终身额度叙事(QuotaCard / StatsGrid / MyFanSupport)

后端:
- votes 表加 @@unique([userId, artistId])(已 apply 到生产 RDS)
- /api/vote 重写:12 票上限 + P2002 ALREADY_VOTED + P2003 NOT_FOUND 兜底
- /api/me 新增 votedArtists[] + voteQuota,移除 dailyQuota
- 新增 ERR.ALREADY_VOTED 错误码

测试:
- DB 层 5/5 + E2E 18/18 通过(scripts/e2e-vote-flow.sh)
- 修复 P2003 FK 违反未识别的 bug

详情见 docs/todo/voting-refactor-完成报告.md 与 voting-refactor-backend-完成报告.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 20:14:57 +08:00

94 lines
2.8 KiB
JavaScript

// 只读探查生产 DB 真实状态 —— 不写任何数据,不改任何 schema。
// 用法:node scripts/inspect-db.mjs
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient({ log: ["error"] });
async function main() {
console.log("=== 生产 DB 真实状态(只读)===\n");
// 1. 用户量级
const userCount = await prisma.user.count();
console.log(`users 总数: ${userCount}`);
// 2. 投票量级
const voteCount = await prisma.vote.count();
console.log(`votes 总数: ${voteCount}`);
// 3. 是否存在重复 (userId, artistId) — 加 unique 前必看
const dup = await prisma.$queryRaw`
SELECT user_id, artist_id, COUNT(*) AS cnt
FROM votes
GROUP BY user_id, artist_id
HAVING cnt > 1
ORDER BY cnt DESC
LIMIT 20
`;
console.log(`重复 (userId, artistId) 行数: ${dup.length}`);
if (dup.length > 0) {
console.log("Top 20 重复样本:");
for (const row of dup) {
console.log(` user=${row.user_id} artist=${row.artist_id} 重复=${row.cnt}`);
}
}
// 4. 单用户最大投票数 — 看是否有人已超过 12 票
const topVoters = await prisma.$queryRaw`
SELECT user_id, COUNT(*) AS total_votes, COUNT(DISTINCT artist_id) AS unique_artists
FROM votes
GROUP BY user_id
ORDER BY total_votes DESC
LIMIT 10
`;
console.log(`\nTop 10 投票最多的用户:`);
for (const row of topVoters) {
console.log(
` user=${row.user_id} total=${row.total_votes} 不同艺人=${row.unique_artists}`,
);
}
// 5. DailyQuota 状态
const dqCount = await prisma.dailyQuota.count();
console.log(`\ndaily_quota 总数: ${dqCount}`);
// 6. FanSupport 状态
const fsCount = await prisma.fanSupport.count();
console.log(`fan_supports 总数: ${fsCount}`);
// 7. ActivityConfig 配置
const config = await prisma.activityConfig.findUnique({ where: { id: 1 } });
console.log(`\nactivity_config:`);
if (config) {
console.log(` voteEnabled=${config.voteEnabled}`);
console.log(` dailyQuota=${config.dailyQuota}`);
console.log(` perArtistLimit=${config.perArtistLimit}`);
console.log(` startAt=${config.startAt.toISOString()}`);
console.log(` endAt=${config.endAt.toISOString()}`);
} else {
console.log(" (空)");
}
// 8. Vote 表当前索引(原始 SQL 探)
const indexes = await prisma.$queryRaw`
SHOW INDEX FROM votes
`;
console.log(`\nvotes 当前索引:`);
for (const idx of indexes) {
console.log(
` ${idx.Key_name} col=${idx.Column_name} seq=${idx.Seq_in_index} unique=${idx.Non_unique === 0 ? "Y" : "N"}`,
);
}
// 9. 服务器版本
const ver = await prisma.$queryRaw`SELECT VERSION() AS v`;
console.log(`\nMySQL 版本: ${ver[0].v}`);
await prisma.$disconnect();
}
main().catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});