iye 71a2672ff6 fix(data,ranking,ui): real dynamic ranking + data sync hardening
数据准确性
- 票数初始为 0,不再用 Math.sqrt 公式造假票
- 排序 tiebreaker 统一为 votes desc + no asc,确保稳定
- store.rank() 与 sortArtists() 行为对齐

Top12 / 出道位
- Top12Bar 仅收纳真正有票的人(votes > 0),0 票时显示"出道位尚未产生"空态
- ArtistCard / RankingRow / SearchModal / MyFanSupport / RankCard 的 inTop12 高亮全部加 votes > 0 守卫
- ArtistFilters 新增"实时排名 / 编号顺序"分段切换 + 首页 sortKey 状态

领奖台 (Top3Podium)
- 1 人有票即可显示领奖台(此前要求 >= 3 人才显示)
- 缺位的 #2/#3 用"虚位以待"占位卡片填充,与正式卡片同 3:4 比例对齐
- 全员 0 票时三张全部显示虚位以待
- 卡片间距拉大到 gap-8 sm:gap-12

排行榜页 (/ranking)
- API 票数与本地乐观投票取 max() 合并,避免 API 落后覆盖本地新票
- 合并后重新排序与赋 rank
- 仅 >= 12 人有票才显示出道线分隔与复活位标记
- 复活位 gapToDebut 计算修正

跨日额度
- selectRemaining 按 quotaDate 判断是否跨日,跨日自动回满额(此前会卡在昨日剩余值)

搜索 (SearchModal)
- 改为订阅 store 拿活的票数,投票后立即反映
- 默认"热门 Top12"只在真正有票时显示,否则降级为"推荐艺人 · 编号顺序"
- 票数显示统一走 formatVotes(0 票不再显示 0.0w)

人物详情
- 36 人真实数据接入,移除全部静态数据(slogan/birthday/cv/themeColor)
- 接入人物小传 docx 数据(年龄/身高/性格/口头禅/技能/赛道/座右铭/长简介)
- 视频区与版心同宽 + 首帧自动作为封面 + 整区点击播放/暂停 + 可拖拽进度条
- 表演图片改为三张氛围图竖向 3:4,左对齐
- 移除分享按钮,投票按钮全宽

个人页 (/me)
- 移除等级/邀请好友/签到/编辑资料等静态数据
- 退出登录按钮在移动端 icon-only 显示(此前 sm:hidden 导致移动端无法登出)
- 我的应援 list 基于真实 myVotesByArtist 派生,凯之类的投票真正同步过去

导航
- 余票徽章未登录态显示 0/0,已登录显示 N/10
- 登录/注册按钮样式与登录后头像胶囊保持一致(紫色实心)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:56:42 +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%