8.4 KiB
8.4 KiB
VoiceHub Agent 行为规范
1. 项目概览
VoiceHub — Nuxt 4 校园广播站点歌管理系统。
- 前端: Nuxt 4 (Vue 3), Tailwind CSS
- 图标: 自定义
Icon.vue组件(内联 SVG),辅以@lucide/vue - 后端: Nuxt Server API (Nitro)
- 数据库: PostgreSQL, Drizzle ORM
- 语言: 后端 TypeScript, Vue SFC 用纯 JavaScript(无
lang="ts")
2. 规范
2.1. 语言
注释、文档、Git 信息均用简体中文。注释中禁止写思考过程等无用内容,只需关键部分。
- CSS 注释只保留关键信息(变量用途、值来源、防误删警告),禁止写设计意图、过程或对比说明(如“取值与现状一致”“视觉零变化”“浅色模式下需清晰可见”“对齐某分支观感”等)
2.2. Vue 组件
- 统一
<script setup>(纯 JS,不加lang="ts",禁止类型注解) - API 调用用
useFetch或$fetch,需错误处理 - 模态框用
<Teleport to="body"> - 图标用
<Icon name="..." />,name 需在Icon.vue中有定义;图标语义必须符合实际功能(如音源控制用ListMusic),禁止使用语义不符的图标 - 项目未开启组件自动导入,模板中使用的自定义组件(含
Icon)必须在<script setup>中显式导入;漏导入不会报错,会被渲染成无内容的原生未知标签(如<icon>),表现为占位但不显示 - 下拉选择统一复用
~/components/UI/Common/CustomSelect.vue;多选使用其multiple模式,禁止为普通业务配置新增原生<select> - 状态管理用 Composables,不用 Pinia
2.3. 后端
- 导入: 项目根目录(如
server/)用~~/,app 目录用~/(~/即app/);drizzle 位于app/drizzle/,服务端导入必须写~/drizzle/db、~/drizzle/schema,禁止~~/drizzle/...(静态检查不报错,部署构建解析失败) - 错误: 用户可见的业务错误统一用
createApiError(statusCode, code, message, data?)(~~/server/utils/apiError.ts),code 取自SERVER_ERROR_CODES;认证错误 401 - 时间戳: 服务端取当前时间统一用
getServerTimestamp()/getServerDate()(~~/server/utils/serverTime.ts),禁止直接写Date.now()/new Date()
2.4. 第三方库
- otplib:
import otplib from 'otplib'然后const { authenticator } = otplib
2.5. 国际化 (i18n)
- 支持
zh-CN(基底,静态内置)与en-US(动态按需加载);词典app/utils/locale/{zh-CN,en-US}.ts结构必须完全一致,新增文案键须两文件同步添加 - 组件取文案用
useLocale()分区 +useLocaleText/useSafeLocale,禁止自行实现callLocale/getNestedMessage等取值函数 - 服务端错误码本地化:服务端
createApiError抛码 → 客户端useServerErrors().localize(err)展示;动态值用第四参{ params: [...] },词典值用{0}/{1}占位符 - 新增错误码须三处同步:
SERVER_ERROR_CODES+ zh/en 的serverErrors(键完全对齐) - 英文长文本须考虑页面布局排布:按钮/Tab/徽章/菜单/开关标签等空间受限位置优先使用缩写(如 Previous → Prev),避免溢出或换行错位;描述性文本(Desc/Placeholder/Hint/Message 等)可自然换行不受限;缩写仅调整 en-US 词典值,键名与词典结构保持不变
3. 项目关键模式
3.1. 音频播放器
useAudioPlayer.ts— 全局状态useAudioPlayerControl.ts—<audio>元素控制、进度拖拽useAudioPlayerSync.ts— 状态同步- 连续失败保护:
consecutiveSkipCount,上限 3 次
3.2. 音源
- 多音源搜索(netease、tencent、bilibili)
- QQ 搜索失败自动降级到网易云,选项卡同步切换
- 搜索结果含
actualMusicPlatform字段
3.3. 字符串匹配
- 归一化唯一权威实现:
app/utils/song-name-normalize.ts的normalizeForMatch(server/utils下同名文件仅转发导出;前端组件禁止内联重复实现) - 口径: 先繁→简(
toSimplifiedChinese,映射表app/utils/data/cjkT2sMap.ts由 OpenCC 数据自动生成、禁止手工编辑),再按单词边界移除feat./ft.,然后移除标点和空格,最后&/&→and
3.4. 专辑详情
AlbumDetailsModal.vue:仅网易云支持,使用AbortController防止竞态- QQ 音乐专辑链接不可点击
3.5. 加载动画
- 加载转圈统一用
~/components/UI/Common/AppSpinner.vue(scoped CSS 实现,不依赖 UnoCSS utility),支持size(直径 px,默认 32)、borderWidth、label属性;复杂加载状态(标题/进度/步骤)用LoadingState.vue(其 circle 类型内部复用 AppSpinner) - 禁止手写
border-*-20/30 border-t-primary rounded-full animate-spin或自建.loading-spinner圆环类;此写法依赖 UnoCSS 生成的 border 工具类,preflight 未设置border-style: solid时整个圆环不可见 - 按钮内加载态可用 Lucide 图标(
Loader2/RefreshCw等)+animate-spin;错误语义色(如网易云红色转圈)保留专用类 - 全局 border 重置:
main.css的*规则含border-style: solid; border-width: 0,勿删除
4. 配置与扩展功能开发规范
4.1. 常量与白名单收敛
- 业务白名单/枚举只允许一个权威定义:服务端放
server/config/constants.ts(如MUSIC_SOURCE_PLATFORMS),前端放共享模块(如app/utils/platforms.ts);其他文件一律 import 引用,禁止重新定义同名数组或映射 - 新增业务实体(平台等)需同步:constants + 前端共享模块 +
app/drizzle/schema.ts默认值 + 迁移文件,缺一不可
4.2. 数据库迁移
- 迁移文件(SQL + snapshot + journal 条目)必须且只能通过
pnpm db:generate自动生成,禁止手工编写 SQL、手工创建 snapshot、手工编辑_journal.json;手工迁移会导致 snapshot 格式与 drizzle-kit 版本不兼容,后续db:generate/db:check/db:migrate均会失败 - 迁移时间戳使用真实生成时刻,禁止随意编造
- 迁移文件命名保持 drizzle-kit 自动生成的时间戳格式,禁止手动改名(改名会导致数据库中已执行的迁移记录无法匹配,产生重复迁移)
4.3. 新增 SystemSettings 字段同步清单
新增字段必须全部同步,遗漏会导致备份能进不能出、初始化缺字段:
app/drizzle/schema.ts(含默认值)server/utils/system-settings-defaults.ts的SYSTEM_SETTINGS_DEFAULTSserver/api/admin/system-settings/index.ts建行分支server/api/admin/backup/restore.post.ts与restore-chunk.post.ts的字段白名单(最易遗漏)- 需公开时加入
PUBLIC_SETTINGS_FIELDS - 迁移文件
4.4. 组合字段交叉校验
- 多个配置字段存在相互约束(如启用列表与排序列表交集非空)时,保存接口必须做交叉一致性校验:提交字段与当前持久化值合并后校验,禁止只独立校验单字段;失败用
createApiError+COMMON_INVALID_PARAMS
4.5. 开关/禁用类功能
- 实现平台/功能开关时,必须梳理该功能的全部调用路径(含既有降级、兜底、跨平台升级路径),在每条路径的源头按配置过滤,禁止只拦截主入口
- 后台任务(如歌词跨平台升级)遇到被禁用的目标应显式跳过并记录日志,不能静默失败
4.6. 配置读取容错
- 读取 DB/外部配置(JSON 数组等)必须容错:非法 JSON、空数组、未知值回退默认值(参考
usePlatformConfig的parsePlatformArray),禁止直接采用解析结果
4.7. 模块级缓存与异步配置 UI
- 模块级共享缓存加载必须防并发(in-flight promise 去重);
refresh类方法加import.meta.server守卫,防止 SSR 污染共享状态 - 依赖异步配置的 UI 在配置加载完成前不渲染(如
v-if="loaded"),避免 SSR 首屏闪烁或禁用项短暂可见
4.8. 抛错与错误展示
- 抛错必须用
Error实例(可附加data),禁止throw { data: ... }字面量 - 客户端用户可见错误统一
useServerErrors().localize(err)展示,禁止直接取error.data.message/error.message(中文消息在英文界面会泄漏)
5. 文件变更提醒
每次完成任务后,如果新增或删除了文件/目录,必须同步更新 README.md 的"项目结构"部分,保持与实际文件系统一致。(注意:数据库迁移文件除外)