iye a9f4799f71
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m26s
feat(db): wire real persistence for votes / users / quota / supports
数据正式落库, 不再仅靠浏览器内存:

prisma/schema.prisma:
- Artist 模型对齐当前前端数据形态:
  * 旧字段 slogan / birthday / cv / themeColor 改为可选 (前端早不用, 但保留兼容历史 seed)
  * 新增 age / gender / motto / personality / catchphrase / skills / track (来自人物小传)
- 注释从 "001 ~ 035" 改 "001 ~ 036"

prisma/seed.ts:
- 整体重写: 从 src/lib/artist-bios.ts 的 ARTIST_SEEDS 灌真实 36 人
- 不再写假数据 (AURORA / LUMI / NEBULA...)
- portrait / videoUrl 不入库 (前端 NEXT_PUBLIC_TOS_DOMAIN 拼接, 换桶不用 reseed)
- ActivityConfig 默认 dailyQuota=10, perArtistLimit=0, voteEnabled=true, 活动期 30 天

src/lib/date-utils.ts (新增):
- startOfUtcDay(): 修复"今日"在 MySQL @db.Date 列与 JS Date 之间的 TZ 漂移
- isSameUtcDay(): 共享给签到判断

修复 P2025 bug (vote / me / signin):
- 用 startOfUtcDay 替代 startOfDay (后者用 setHours 取本地午夜,
  对 @db.Date 列会因 TZ 漂移导致 upsert 后再用 userId_date 复合键查找失败)
- /api/vote 的扣额度从 userId_date 改用 dq.id 主键 update, 双保险
- 三个路由的 startOfDay 重复实现合并到 lib/date-utils

E2E 验证 (curl):
  登录 → 投 5 给 002 → 余 5 ✓
  投 3 给 003 → 余 2 / totalVotes 8 ✓
  /api/me supports 反映 002+003 真实 voteTotal ✓
  超额 (5 票 余 2) → 409 QUOTA_EXHAUSTED ✓
  /api/ranking 票数实时反映 DB ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:32:38 +08:00

CYBER ✦ STAR

虚拟偶像 Top12 出道企划 · 投票网页

技术栈

选型
框架 Next.js 16App Router· React 19 · TypeScript
样式 Tailwind CSS v4CSS-first @theme)· Framer Motion
字体 Megrim / Audiowide / Cinzel / Inter全部 SIL OFL 商用免费)
数据库 MySQL 8 · Prisma 6 ORM
缓存 / 限流 Redisioredis,未配置时降级内存)
身份 Auth.js v5手机号 OTP · 可扩展微信 / QQ
校验 Zod
部署 火山引擎 ECS · 火山引擎 TOS对象存储

本地启动

pnpm install            # 安装依赖(自动 prisma generate
cp .env.example .env    # 配置环境变量(首次)
pnpm dev                # http://localhost:3000

未配置数据库时,前端页面会回退到 src/lib/mock-data.ts 的 35 位艺人 mock 数据UI 完全可用。

数据库初始化(部署时)

# 1. 创建 MySQL 库 + 在 .env 设置 DATABASE_URL
# 2. 推送 schema 到数据库
pnpm db:push

# 3. 灌入 35 位艺人 + 活动配置
pnpm db:seed

# 可视化管理(可选)
pnpm db:studio

关键脚本

命令 说明
pnpm dev 开发服务器Turbopack
pnpm build 生产构建(先 prisma generate
pnpm start 生产服务器
pnpm lint ESLint
pnpm db:push 把 schema 推到数据库
pnpm db:migrate 生成迁移脚本
pnpm db:seed 灌入种子数据
pnpm db:studio Prisma StudioGUI

项目结构

prisma/
  schema.prisma         # 数据库 schema10 个模型)
  seed.ts               # 初始化 35 位艺人

src/
  app/
    layout.tsx          # 根布局(含氛围装饰层)
    page.tsx            # 首页Hero PV + Top12 + 35 卡片)
    artist/[id]/        # 艺人详情页
    ranking/            # 排行榜Top3 podium + 出道线 + 候补区)
    me/                 # 个人中心
    login/              # 登录页(手机号 OTP
    api/
      artists/          # GET 艺人列表 / 单个详情
      ranking/          # GET 实时排名
      vote/             # POST 投票(含风控限流 + 事务)
      me/               # GET 当前用户 / POST 签到
      auth/             # NextAuth handlers + send-otp
  components/
    Logo / Navigation / Footer / NavLinks
    HeroBanner / Top12Bar / ArtistFilters
    VoteModal / FloatingVoteButton / LiveBadge
    ui/                 # Button / Countdown
    cards/              # ArtistCard / ArtistPortrait
    artist/             # 详情页组件(视频 / 画廊 / 排名卡等)
    ranking/            # 排行榜组件
    me/                 # 个人中心组件
  hooks/
    useRanking.ts       # 实时排名轮询 hook
  lib/
    prisma / redis / rate-limit
    auth / current-user / api-response
    cn / mock-data / mock-user
  types/
    artist.ts

设计资料

  • 交互原型:../交互原型线框图.html
  • 视觉规范:../视觉规范.html · v1.1 紫调主导
  • 需求文档:../需求分析文档.md
  • 参考视觉:../参考图.png

团队需配置的外部服务

服务 用途 环境变量
MySQL火山 RDS 主数据库 DATABASE_URL
Redis火山实例 限流 / OTP 缓存 / 实时聚合 REDIS_URL
TOS火山对象存储 立绘 / 视频 / 头像 TOS_*
短信网关 手机号 OTP SMS_*
微信开放平台 微信扫码登录 WECHAT_APP_ID WECHAT_APP_SECRET
hCaptcha 反作弊验证码 HCAPTCHA_*

详见 .env.example。所有这些都用 TODO 注释标记在代码中,可灰度配置 —— 没配置时功能自动降级

开发态便利

  • 登录 /login 页面下,开发环境接受万能验证码 123456
  • mock 用户 API 调用前自动落到 cs_user_id cookie可在开发面板设置
  • mock 数据 :未配 DB 时前端自动使用 mock-data.ts 的 35 位艺人
  • 实时排名 API 不可用时静默回退到 mock 数据

设计原则

参考 ../视觉规范.html 的 16 个章节,核心:

  • 紫罗兰为主调色(不是蓝),承担 CTA / 激活态 / 描边
  • 装饰星标 ✦ 用于 Logo 中间,体现品牌签
  • TOP12 头像方形圆角(不是圆形)— "明星卡片"质感
  • VOTE NOW 紫色侧栏面板(不是普通按钮)— 视觉锚点
  • 背景始终在深紫黑系,星点 + 紫雾装饰层
  • 投票交互必须有动画仪式感
Description
No description provided
Readme 14 MiB
Languages
TypeScript 82.5%
JavaScript 12.7%
CSS 2.3%
Shell 1.6%
Dockerfile 0.9%